diff options
Diffstat (limited to 'src')
503 files changed, 9636 insertions, 7062 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 0409faee1e..2616eb8638 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,6 +2,10 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +# Pattern rule to print variables, e.g. make print-top_srcdir +print-%: + @echo $* = $($*) + DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS) @@ -20,9 +24,8 @@ else LIBUNIVALUE = $(UNIVALUE_LIBS) endif -BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) +BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/secp256k1/include $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) -BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) LIBBITCOIN_SERVER=libbitcoin_server.a @@ -93,15 +96,21 @@ endif if BUILD_BITCOIN_CLI bin_PROGRAMS += bitcoin-cli endif + if BUILD_BITCOIN_TX bin_PROGRAMS += bitcoin-tx endif + if ENABLE_WALLET if BUILD_BITCOIN_WALLET bin_PROGRAMS += bitcoin-wallet endif endif +if BUILD_BITCOIN_UTIL + bin_PROGRAMS += bitcoin-util +endif + .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ @@ -153,6 +162,7 @@ BITCOIN_CORE_H = \ key_io.h \ logging.h \ logging/timer.h \ + mapport.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -224,6 +234,7 @@ BITCOIN_CORE_H = \ util/error.h \ util/fees.h \ util/golombrice.h \ + util/hasher.h \ util/macros.h \ util/memory.h \ util/message.h \ @@ -236,6 +247,7 @@ BITCOIN_CORE_H = \ util/system.h \ util/threadnames.h \ util/time.h \ + util/trace.h \ util/translation.h \ util/ui_change_type.h \ util/url.h \ @@ -250,6 +262,7 @@ BITCOIN_CORE_H = \ wallet/context.h \ wallet/crypter.h \ wallet/db.h \ + wallet/dump.h \ wallet/feebumper.h \ wallet/fees.h \ wallet/ismine.h \ @@ -299,14 +312,14 @@ libbitcoin_server_a_SOURCES = \ index/blockfilterindex.cpp \ index/txindex.cpp \ init.cpp \ - interfaces/chain.cpp \ - interfaces/node.cpp \ + mapport.cpp \ miner.cpp \ net.cpp \ net_processing.cpp \ node/coin.cpp \ node/coinstats.cpp \ node/context.cpp \ + node/interfaces.cpp \ node/psbt.cpp \ node/transaction.cpp \ node/ui_interface.cpp \ @@ -359,13 +372,14 @@ endif libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(SQLITE_CFLAGS) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ - interfaces/wallet.cpp \ wallet/coincontrol.cpp \ wallet/context.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ + wallet/dump.cpp \ wallet/feebumper.cpp \ wallet/fees.cpp \ + wallet/interfaces.cpp \ wallet/load.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ @@ -408,6 +422,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \ crypto/hmac_sha512.h \ crypto/poly1305.h \ crypto/poly1305.cpp \ + crypto/muhash.h \ + crypto/muhash.cpp \ crypto/ripemd160.cpp \ crypto/ripemd160.h \ crypto/sha1.cpp \ @@ -540,6 +556,7 @@ libbitcoin_util_a_SOURCES = \ util/bytevectorhash.cpp \ util/error.cpp \ util/fees.cpp \ + util/hasher.cpp \ util/system.cpp \ util/message.cpp \ util/moneystr.cpp \ @@ -596,7 +613,7 @@ bitcoin_bin_ldadd = \ $(LIBMEMENV) \ $(LIBSECP256K1) -bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) +bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) bitcoind_SOURCES = $(bitcoin_daemon_sources) bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags) @@ -662,6 +679,27 @@ bitcoin_wallet_SOURCES += bitcoin-wallet-res.rc endif # +# bitcoin-util binary # +bitcoin_util_SOURCES = bitcoin-util.cpp +bitcoin_util_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +bitcoin_util_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +bitcoin_util_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +if TARGET_WINDOWS +bitcoin_util_SOURCES += bitcoin-util-res.rc +endif + +bitcoin_util_LDADD = \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBSECP256K1) + +bitcoin_util_LDADD += $(BOOST_LIBS) +# + # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h @@ -742,13 +780,13 @@ endif if GLIBC_BACK_COMPAT @echo "Checking glibc back compat..." - $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) + $(AM_V_at) CPPFILT=$(CPPFILT) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) endif check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security..." - $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS) + $(AM_V_at) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS) endif if EMBEDDED_LEVELDB diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index beb3f8dfd2..56b8ca8ce6 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -74,7 +74,7 @@ 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) $(SQLITE_LIBS) +bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_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.crc32c.include b/src/Makefile.crc32c.include index 802b3a2e4b..113272e65e 100644 --- a/src/Makefile.crc32c.include +++ b/src/Makefile.crc32c.include @@ -41,7 +41,7 @@ crc32c_libcrc32c_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crc32c_libcrc32c_a_SOURCES = crc32c_libcrc32c_a_SOURCES += crc32c/include/crc32c/crc32c.h crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_arm64.h -crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_arm64_linux_check.h +crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_arm64_check.h crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_internal.h crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_prefetch.h crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_read_le.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f46310a603..969f0ca411 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -78,6 +78,7 @@ QT_MOC_CPP = \ qt/moc_transactiondesc.cpp \ qt/moc_transactiondescdialog.cpp \ qt/moc_transactionfilterproxy.cpp \ + qt/moc_transactionoverviewwidget.cpp \ qt/moc_transactiontablemodel.cpp \ qt/moc_transactionview.cpp \ qt/moc_utilitydialog.cpp \ @@ -151,6 +152,7 @@ BITCOIN_QT_H = \ qt/transactiondesc.h \ qt/transactiondescdialog.h \ qt/transactionfilterproxy.h \ + qt/transactionoverviewwidget.h \ qt/transactionrecord.h \ qt/transactiontablemodel.h \ qt/transactionview.h \ @@ -320,7 +322,7 @@ if ENABLE_ZMQ 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) \ + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \ $(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 diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index c05dd38737..a6a857d952 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -55,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) 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) \ + $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \ $(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) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 87166ecb79..e9f9b73abe 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -2,171 +2,15 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -FUZZ_TARGETS = \ - test/fuzz/addition_overflow \ - test/fuzz/addr_info_deserialize \ - test/fuzz/addrdb \ - test/fuzz/address_deserialize \ - test/fuzz/addrman \ - test/fuzz/addrman_deserialize \ - test/fuzz/asmap \ - test/fuzz/asmap_direct \ - test/fuzz/autofile \ - test/fuzz/banentry_deserialize \ - test/fuzz/banman \ - test/fuzz/base_encode_decode \ - test/fuzz/bech32 \ - test/fuzz/block \ - test/fuzz/block_deserialize \ - test/fuzz/block_file_info_deserialize \ - test/fuzz/block_filter_deserialize \ - test/fuzz/block_header \ - test/fuzz/block_header_and_short_txids_deserialize \ - test/fuzz/blockfilter \ - test/fuzz/blockheader_deserialize \ - test/fuzz/blocklocator_deserialize \ - test/fuzz/blockmerkleroot \ - test/fuzz/blocktransactions_deserialize \ - test/fuzz/blocktransactionsrequest_deserialize \ - test/fuzz/blockundo_deserialize \ - test/fuzz/bloom_filter \ - test/fuzz/bloomfilter_deserialize \ - test/fuzz/buffered_file \ - test/fuzz/chain \ - test/fuzz/checkqueue \ - test/fuzz/coins_deserialize \ - test/fuzz/coins_view \ - test/fuzz/connman \ - test/fuzz/crypto \ - test/fuzz/crypto_aes256 \ - test/fuzz/crypto_aes256cbc \ - test/fuzz/crypto_chacha20 \ - test/fuzz/crypto_chacha20_poly1305_aead \ - test/fuzz/crypto_common \ - test/fuzz/crypto_hkdf_hmac_sha256_l32 \ - test/fuzz/crypto_poly1305 \ - test/fuzz/cuckoocache \ - test/fuzz/decode_tx \ - test/fuzz/descriptor_parse \ - test/fuzz/diskblockindex_deserialize \ - test/fuzz/eval_script \ - test/fuzz/fee_rate \ - test/fuzz/fee_rate_deserialize \ - test/fuzz/fees \ - test/fuzz/flat_file_pos_deserialize \ - test/fuzz/flatfile \ - test/fuzz/float \ - test/fuzz/golomb_rice \ - test/fuzz/hex \ - test/fuzz/http_request \ - test/fuzz/integer \ - test/fuzz/inv_deserialize \ - test/fuzz/key \ - test/fuzz/key_io \ - test/fuzz/key_origin_info_deserialize \ - test/fuzz/kitchen_sink \ - test/fuzz/load_external_block_file \ - test/fuzz/locale \ - test/fuzz/merkle_block_deserialize \ - test/fuzz/merkleblock \ - 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 \ - test/fuzz/out_point_deserialize \ - test/fuzz/p2p_transport_deserializer \ - test/fuzz/parse_hd_keypath \ - test/fuzz/parse_iso8601 \ - test/fuzz/parse_numbers \ - test/fuzz/parse_script \ - test/fuzz/parse_univalue \ - test/fuzz/partial_merkle_tree_deserialize \ - test/fuzz/partially_signed_transaction_deserialize \ - test/fuzz/policy_estimator \ - test/fuzz/policy_estimator_io \ - test/fuzz/pow \ - test/fuzz/prefilled_transaction_deserialize \ - test/fuzz/prevector \ - test/fuzz/primitives_transaction \ - test/fuzz/process_message \ - test/fuzz/process_message_addr \ - test/fuzz/process_message_block \ - test/fuzz/process_message_blocktxn \ - test/fuzz/process_message_cmpctblock \ - test/fuzz/process_message_feefilter \ - test/fuzz/process_message_filteradd \ - test/fuzz/process_message_filterclear \ - test/fuzz/process_message_filterload \ - test/fuzz/process_message_getaddr \ - test/fuzz/process_message_getblocks \ - test/fuzz/process_message_getblocktxn \ - test/fuzz/process_message_getdata \ - test/fuzz/process_message_getheaders \ - test/fuzz/process_message_headers \ - test/fuzz/process_message_inv \ - test/fuzz/process_message_mempool \ - test/fuzz/process_message_notfound \ - test/fuzz/process_message_ping \ - test/fuzz/process_message_pong \ - test/fuzz/process_message_sendcmpct \ - test/fuzz/process_message_sendheaders \ - test/fuzz/process_message_tx \ - test/fuzz/process_message_verack \ - test/fuzz/process_message_version \ - test/fuzz/process_messages \ - test/fuzz/protocol \ - test/fuzz/psbt \ - test/fuzz/psbt_input_deserialize \ - test/fuzz/psbt_output_deserialize \ - test/fuzz/pub_key_deserialize \ - test/fuzz/random \ - test/fuzz/rbf \ - test/fuzz/rolling_bloom_filter \ - test/fuzz/script \ - test/fuzz/script_bitcoin_consensus \ - test/fuzz/script_descriptor_cache \ - 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 \ - test/fuzz/string \ - test/fuzz/strprintf \ - test/fuzz/sub_net_deserialize \ - test/fuzz/system \ - test/fuzz/timedata \ - test/fuzz/transaction \ - test/fuzz/tx_in \ - 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 - if ENABLE_FUZZ -noinst_PROGRAMS += $(FUZZ_TARGETS:=) +noinst_PROGRAMS += test/fuzz/fuzz else bin_PROGRAMS += test/test_bitcoin endif TEST_SRCDIR = test TEST_BINARY=test/test_bitcoin$(EXEEXT) +FUZZ_BINARY=test/fuzz/fuzz$(EXEEXT) JSON_TEST_FILES = \ test/data/script_tests.json \ @@ -323,7 +167,7 @@ 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) $(SQLITE_LIBS) +test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static if ENABLE_ZMQ @@ -334,935 +178,106 @@ 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 = $(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 = $(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 = $(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 = $(FUZZ_SUITE_LDFLAGS_COMMON) -test_fuzz_address_deserialize_SOURCES = test/fuzz/deserialize.cpp - -test_fuzz_addrman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -test_fuzz_addrman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_fuzz_addrman_LDADD = $(FUZZ_SUITE_LD_COMMON) -test_fuzz_addrman_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) -test_fuzz_addrman_SOURCES = test/fuzz/addrman.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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(FUZZ_SUITE_LDFLAGS_COMMON) -test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp - -test_fuzz_connman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -test_fuzz_connman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_fuzz_connman_LDADD = $(FUZZ_SUITE_LD_COMMON) -test_fuzz_connman_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) -test_fuzz_connman_SOURCES = test/fuzz/connman.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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(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 = $(FUZZ_SUITE_LDFLAGS_COMMON) -test_fuzz_uint256_deserialize_SOURCES = test/fuzz/deserialize.cpp +test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_fuzz_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) +test_fuzz_fuzz_SOURCES = \ + test/fuzz/addition_overflow.cpp \ + test/fuzz/addrdb.cpp \ + test/fuzz/addrman.cpp \ + test/fuzz/asmap.cpp \ + test/fuzz/asmap_direct.cpp \ + test/fuzz/autofile.cpp \ + test/fuzz/banman.cpp \ + test/fuzz/base_encode_decode.cpp \ + test/fuzz/bech32.cpp \ + test/fuzz/block.cpp \ + test/fuzz/block_header.cpp \ + test/fuzz/blockfilter.cpp \ + test/fuzz/bloom_filter.cpp \ + test/fuzz/buffered_file.cpp \ + test/fuzz/chain.cpp \ + test/fuzz/checkqueue.cpp \ + test/fuzz/coins_view.cpp \ + test/fuzz/connman.cpp \ + test/fuzz/crypto.cpp \ + test/fuzz/crypto_aes256.cpp \ + test/fuzz/crypto_aes256cbc.cpp \ + test/fuzz/crypto_chacha20.cpp \ + test/fuzz/crypto_chacha20_poly1305_aead.cpp \ + test/fuzz/crypto_common.cpp \ + test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \ + test/fuzz/crypto_poly1305.cpp \ + test/fuzz/cuckoocache.cpp \ + test/fuzz/data_stream.cpp \ + test/fuzz/decode_tx.cpp \ + test/fuzz/descriptor_parse.cpp \ + test/fuzz/deserialize.cpp \ + test/fuzz/eval_script.cpp \ + test/fuzz/fee_rate.cpp \ + test/fuzz/fees.cpp \ + test/fuzz/flatfile.cpp \ + test/fuzz/float.cpp \ + test/fuzz/golomb_rice.cpp \ + test/fuzz/hex.cpp \ + test/fuzz/http_request.cpp \ + test/fuzz/integer.cpp \ + test/fuzz/key.cpp \ + test/fuzz/key_io.cpp \ + test/fuzz/kitchen_sink.cpp \ + test/fuzz/load_external_block_file.cpp \ + test/fuzz/locale.cpp \ + test/fuzz/merkleblock.cpp \ + test/fuzz/message.cpp \ + test/fuzz/muhash.cpp \ + test/fuzz/multiplication_overflow.cpp \ + test/fuzz/net.cpp \ + test/fuzz/net_permissions.cpp \ + test/fuzz/netaddress.cpp \ + test/fuzz/node_eviction.cpp \ + test/fuzz/p2p_transport_deserializer.cpp \ + test/fuzz/parse_hd_keypath.cpp \ + test/fuzz/parse_iso8601.cpp \ + test/fuzz/parse_numbers.cpp \ + test/fuzz/parse_script.cpp \ + test/fuzz/parse_univalue.cpp \ + test/fuzz/policy_estimator.cpp \ + test/fuzz/policy_estimator_io.cpp \ + test/fuzz/pow.cpp \ + test/fuzz/prevector.cpp \ + test/fuzz/primitives_transaction.cpp \ + test/fuzz/process_message.cpp \ + test/fuzz/process_messages.cpp \ + test/fuzz/protocol.cpp \ + test/fuzz/psbt.cpp \ + test/fuzz/random.cpp \ + test/fuzz/rbf.cpp \ + test/fuzz/rolling_bloom_filter.cpp \ + test/fuzz/script.cpp \ + test/fuzz/script_assets_test_minimizer.cpp \ + test/fuzz/script_bitcoin_consensus.cpp \ + test/fuzz/script_descriptor_cache.cpp \ + test/fuzz/script_flags.cpp \ + test/fuzz/script_interpreter.cpp \ + test/fuzz/script_ops.cpp \ + test/fuzz/script_sigcache.cpp \ + test/fuzz/script_sign.cpp \ + test/fuzz/scriptnum_ops.cpp \ + test/fuzz/secp256k1_ec_seckey_import_export_der.cpp \ + test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp \ + test/fuzz/signature_checker.cpp \ + test/fuzz/signet.cpp \ + test/fuzz/span.cpp \ + test/fuzz/spanparsing.cpp \ + test/fuzz/string.cpp \ + test/fuzz/strprintf.cpp \ + test/fuzz/system.cpp \ + test/fuzz/timedata.cpp \ + test/fuzz/transaction.cpp \ + test/fuzz/tx_in.cpp \ + test/fuzz/tx_out.cpp \ + test/fuzz/txrequest.cpp endif # ENABLE_FUZZ @@ -1309,6 +324,11 @@ if EMBEDDED_UNIVALUE $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check endif +if ENABLE_FUZZ_LINK_ALL +all-local: $(FUZZ_BINARY) + bash ./test/fuzz/danger_link_all.sh +endif + %.cpp.test: %.cpp @echo Running tests: `cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $< $(AM_V_at)$(TEST_BINARY) --catch_system_errors=no -l test_suite -t "`cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" -- DEBUG_LOG_OUT > $<.log 2>&1 || (cat $<.log && false) diff --git a/src/Makefile.test_fuzz.include b/src/Makefile.test_fuzz.include index 4e858979fe..75fe68fcd1 100644 --- a/src/Makefile.test_fuzz.include +++ b/src/Makefile.test_fuzz.include @@ -16,6 +16,7 @@ libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAG libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libtest_fuzz_a_SOURCES = \ test/fuzz/fuzz.cpp \ + test/fuzz/util.cpp \ $(TEST_FUZZ_H) LIBTEST_FUZZ += $(LIBBITCOIN_SERVER) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 27f22826a9..8f77ed35ce 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/addrdb.h b/src/addrdb.h index 4ac0e3e1b5..8953ebb169 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/addrman.cpp b/src/addrman.cpp index 7636c6bad2..f91121f156 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -9,6 +9,8 @@ #include <logging.h> #include <serialize.h> +#include <cmath> + int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const { uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash(); @@ -617,7 +619,7 @@ CAddrInfo CAddrMan::SelectTriedCollision_() return CAddrInfo(); } - CAddrInfo& newInfo = mapInfo[id_new]; + const CAddrInfo& newInfo = mapInfo[id_new]; // which tried bucket to move the entry to int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap); diff --git a/src/attributes.h b/src/attributes.h index 45099bd8b8..9957bcd84b 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -1,22 +1,19 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_ATTRIBUTES_H #define BITCOIN_ATTRIBUTES_H -#if defined(__has_cpp_attribute) -# if __has_cpp_attribute(nodiscard) -# define NODISCARD [[nodiscard]] -# endif -#endif -#ifndef NODISCARD -# if defined(_MSC_VER) && _MSC_VER >= 1700 -# define NODISCARD _Check_return_ +#if defined(__clang__) +# if __has_attribute(lifetimebound) +# define LIFETIMEBOUND [[clang::lifetimebound]] # else -# define NODISCARD __attribute__((warn_unused_result)) +# define LIFETIMEBOUND # endif +#else +# define LIFETIMEBOUND #endif #endif // BITCOIN_ATTRIBUTES_H diff --git a/src/banman.cpp b/src/banman.cpp index 995fef3d07..49bf6c43dc 100644 --- a/src/banman.cpp +++ b/src/banman.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/base58.cpp b/src/base58.cpp index 0dc6044145..65e373283c 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 The Bitcoin Core developers +// Copyright (c) 2014-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -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, }; -NODISCARD static 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)) @@ -141,7 +141,7 @@ std::string EncodeBase58Check(Span<const unsigned char> input) return EncodeBase58(vch); } -NODISCARD static 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 468c3e2589..9ba5af73e0 100644 --- a/src/base58.h +++ b/src/base58.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -29,7 +29,7 @@ std::string EncodeBase58(Span<const unsigned char> input); * Decode a base58-encoded string (str) into a byte vector (vchRet). * return true if decoding is successful. */ -NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len); +[[nodiscard]] bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len); /** * Encode a byte span into a base58-encoded string, including checksum @@ -40,6 +40,6 @@ std::string EncodeBase58Check(Span<const unsigned char> input); * Decode a base58-encoded string (str) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len); +[[nodiscard]] bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len); #endif // BITCOIN_BASE58_H diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp index 18cb5de196..6f6b4e3bfa 100644 --- a/src/bench/base58.cpp +++ b/src/bench/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 99a7ad237b..af5a82f69f 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp index 913e0f8d57..a6f4eec4ca 100644 --- a/src/bench/chacha20.cpp +++ b/src/bench/chacha20.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp index 3b1d3e697a..e994279a4d 100644 --- a/src/bench/chacha_poly_aead.cpp +++ b/src/bench/chacha_poly_aead.cpp @@ -31,12 +31,15 @@ static void CHACHA20_POLY1305_AEAD(benchmark::Bench& bench, size_t buffersize, b uint32_t len = 0; bench.batch(buffersize).unit("byte").run([&] { // encrypt or decrypt the buffer with a static key - assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true)); + const bool crypt_ok_1 = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true); + assert(crypt_ok_1); if (include_decryption) { // if we decrypt, include the GetLength - assert(aead.GetLength(&len, seqnr_aad, aad_pos, in.data())); - assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true)); + const bool get_length_ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); + assert(get_length_ok); + const bool crypt_ok_2 = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true); + assert(crypt_ok_2); } // increase main sequence number diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index ffa772d8c1..d7b8c1badc 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -10,8 +10,6 @@ #include <random.h> #include <util/system.h> -#include <boost/thread/thread.hpp> - #include <vector> static const size_t BATCHES = 101; @@ -44,12 +42,9 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench) void swap(PrevectorJob& x){p.swap(x.p);}; }; CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE}; - boost::thread_group tg; // 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();}); - } + queue.StartWorkerThreads(GetNumCores() - 1); // create all the data once, then submit copies in the benchmark. FastRandomContext insecure_rand(true); @@ -70,8 +65,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench) // it is done explicitly here for clarity control.Wait(); }); - tg.interrupt_all(); - tg.join_all(); + queue.StopWorkerThreads(); ECC_Stop(); } BENCHMARK(CCheckQueueSpeedPrevectorJob); diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 99aafd8dfc..3abfbfd784 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -42,20 +42,19 @@ static void CoinSelection(benchmark::Bench& bench) } addCoin(3 * COIN, wallet, wtxs); - // Create groups - std::vector<OutputGroup> groups; + // Create coins + std::vector<COutput> coins; for (const auto& wtx : wtxs) { - COutput output(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */); - groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0); + coins.emplace_back(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */); } const CoinEligibilityFilter filter_standard(1, 6, 0); - const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0); + const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0, false); bench.run([&] { std::set<CInputCoin> setCoinsRet; CAmount nValueRet; bool bnb_used; - bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used); + bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params, bnb_used); assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); @@ -75,7 +74,8 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup> tx.vout.resize(nInput + 1); tx.vout[nInput].nValue = nValue; std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); - set.emplace_back(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0); + set.emplace_back(); + set.back().Insert(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false); wtxn.emplace_back(std::move(wtx)); } // Copied from src/wallet/test/coinselector_tests.cpp diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 65d16d47d8..30fe11be6b 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -4,6 +4,7 @@ #include <bench/bench.h> +#include <crypto/muhash.h> #include <crypto/ripemd160.h> #include <crypto/sha1.h> #include <crypto/sha256.h> @@ -105,6 +106,54 @@ static void FastRandom_1bit(benchmark::Bench& bench) }); } +static void MuHash(benchmark::Bench& bench) +{ + MuHash3072 acc; + unsigned char key[32] = {0}; + int i = 0; + bench.run([&] { + key[0] = ++i; + acc *= MuHash3072(key); + }); +} + +static void MuHashMul(benchmark::Bench& bench) +{ + MuHash3072 acc; + FastRandomContext rng(true); + MuHash3072 muhash{rng.randbytes(32)}; + + bench.run([&] { + acc *= muhash; + }); +} + +static void MuHashDiv(benchmark::Bench& bench) +{ + MuHash3072 acc; + FastRandomContext rng(true); + MuHash3072 muhash{rng.randbytes(32)}; + + for (size_t i = 0; i < bench.epochIterations(); ++i) { + acc *= muhash; + } + + bench.run([&] { + acc /= muhash; + }); +} + +static void MuHashPrecompute(benchmark::Bench& bench) +{ + MuHash3072 acc; + FastRandomContext rng(true); + std::vector<unsigned char> key{rng.randbytes(32)}; + + bench.run([&] { + MuHash3072{key}; + }); +} + BENCHMARK(RIPEMD160); BENCHMARK(SHA1); BENCHMARK(SHA256); @@ -116,3 +165,8 @@ BENCHMARK(SipHash_32b); BENCHMARK(SHA256D64_1024); BENCHMARK(FastRandom_32bit); BENCHMARK(FastRandom_1bit); + +BENCHMARK(MuHash); +BENCHMARK(MuHashMul); +BENCHMARK(MuHashDiv); +BENCHMARK(MuHashPrecompute); diff --git a/src/bench/gcs_filter.cpp b/src/bench/gcs_filter.cpp index ef83242e41..607e4392b7 100644 --- a/src/bench/gcs_filter.cpp +++ b/src/bench/gcs_filter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/bench/hashpadding.cpp b/src/bench/hashpadding.cpp index 309cae3723..753c8c2881 100644 --- a/src/bench/hashpadding.cpp +++ b/src/bench/hashpadding.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2018 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/bench/lockedpool.cpp b/src/bench/lockedpool.cpp index 32b060a15a..b6d8824aba 100644 --- a/src/bench/lockedpool.cpp +++ b/src/bench/lockedpool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index 1b9e428c9d..db9a5661fd 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index 89233e390c..9b862b735c 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/bench/poly1305.cpp b/src/bench/poly1305.cpp index d8db99e7d4..cdef97c0ea 100644 --- a/src/bench/poly1305.cpp +++ b/src/bench/poly1305.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp index 924b6c0f5a..f4fabedab6 100644 --- a/src/bench/prevector.cpp +++ b/src/bench/prevector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2018 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,24 +9,16 @@ #include <bench/bench.h> -// GCC 4.8 is missing some C++11 type_traits, -// https://www.gnu.org/software/gcc/gcc-5/changes.html -#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 -#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor -#else -#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible -#endif - struct nontrivial_t { int x; nontrivial_t() :x(-1) {} SERIALIZE_METHODS(nontrivial_t, obj) { READWRITE(obj.x); } }; -static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value, +static_assert(!std::is_trivially_default_constructible<nontrivial_t>::value, "expected nontrivial_t to not be trivially constructible"); typedef unsigned char trivial_t; -static_assert(IS_TRIVIALLY_CONSTRUCTIBLE<trivial_t>::value, +static_assert(std::is_trivially_default_constructible<trivial_t>::value, "expected trivial_t to be trivially constructible"); template <typename T> diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp index 9b43951e6e..997ab56549 100644 --- a/src/bench/rollingbloom.cpp +++ b/src/bench/rollingbloom.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp index 4b45264a3c..45ed9f60dc 100644 --- a/src/bench/rpc_blockchain.cpp +++ b/src/bench/rpc_blockchain.cpp @@ -7,12 +7,15 @@ #include <rpc/blockchain.h> #include <streams.h> +#include <test/util/setup_common.h> #include <validation.h> #include <univalue.h> static void BlockToJsonVerbose(benchmark::Bench& bench) { + TestingSetup test_setup{}; + CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION); char a = '\0'; stream.write(&a, 1); // Prevent compaction diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp index 1ff41765cf..f1eeef8885 100644 --- a/src/bench/rpc_mempool.cpp +++ b/src/bench/rpc_mempool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/bench/util_time.cpp b/src/bench/util_time.cpp index fad179eb87..afc733482e 100644 --- a/src/bench/util_time.cpp +++ b/src/bench/util_time.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 9af0b502eb..e3f6b35a7d 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -24,7 +24,7 @@ static void VerifyScriptBench(benchmark::Bench& bench) const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH; const int witnessversion = 0; - // Keypair. + // Key pair. CKey key; static const std::array<unsigned char, 32> vchKey = { { diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp index b3b73284d8..b385cec085 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -24,21 +24,19 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE; - NodeContext node; - std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node); - CWallet wallet{chain.get(), "", CreateMockWalletDatabase()}; + CWallet wallet{test_setup.m_node.chain.get(), "", CreateMockWalletDatabase()}; { wallet.SetupLegacyScriptPubKeyMan(); bool first_run; if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false); } - auto handler = chain->handleNotifications({&wallet, [](CWallet*) {}}); + auto handler = test_setup.m_node.chain->handleNotifications({&wallet, [](CWallet*) {}}); const Optional<std::string> address_mine{add_mine ? Optional<std::string>{getnewaddress(wallet)} : nullopt}; if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY); for (int i = 0; i < 100; ++i) { - generatetoaddress(test_setup.m_node, address_mine.get_value_or(ADDRESS_WATCHONLY)); + generatetoaddress(test_setup.m_node, address_mine.value_or(ADDRESS_WATCHONLY)); generatetoaddress(test_setup.m_node, ADDRESS_WATCHONLY); } SyncWithValidationInterfaceQueue(); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index ef4641cb63..fa41208a31 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -21,6 +21,7 @@ #include <util/url.h> #include <algorithm> +#include <cmath> #include <functional> #include <memory> #include <stdio.h> @@ -57,9 +58,9 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC 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("-generate", strprintf("Generate blocks immediately, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-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); + argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); SetupChainParamsBaseOptions(argsman); argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -309,7 +310,8 @@ private: } return UNKNOWN_NETWORK; } - uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level + uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level + bool m_is_help_requested{false}; //!< Optional user-supplied arg to print help documentation 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; } @@ -349,6 +351,62 @@ private: const double milliseconds{round(1000 * seconds)}; return milliseconds > 999999 ? "-" : ToString(milliseconds); } + const UniValue NetinfoHelp() + { + return std::string{ + "-netinfo level|\"help\" \n\n" + "Returns a network peer connections dashboard with information from the remote server.\n" + "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n" + "An optional integer argument from 0 to 4 can be passed for different peers listings.\n" + "Pass \"help\" to see this detailed help documentation.\n" + "If more than one argument is passed, only the first one is read and parsed.\n" + "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n" + "Arguments:\n" + "1. level (integer 0-4, optional) Specify the info level of the peers dashboard (default 0):\n" + " 0 - Connection counts and local addresses\n" + " 1 - Like 0 but with a peers listing (without address or version columns)\n" + " 2 - Like 1 but with an address column\n" + " 3 - Like 1 but with a version column\n" + " 4 - Like 1 but with both address and version columns\n" + "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n" + "Result:\n\n" + "* The peers listing in levels 1-4 displays all of the peers sorted by direction and minimum ping time:\n\n" + " Column Description\n" + " ------ -----------\n" + " <-> Direction\n" + " \"in\" - inbound connections are those initiated by the peer\n" + " \"out\" - outbound connections are those initiated by us\n" + " type Type of peer connection\n" + " \"full\" - full relay, the default\n" + " \"block\" - block relay; like full relay but does not relay transactions or addresses\n" + " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", or \"cjdns\")\n" + " mping Minimum observed ping time, in milliseconds (ms)\n" + " ping Last observed ping time, in milliseconds (ms)\n" + " send Time since last message sent to the peer, in seconds\n" + " recv Time since last message received from the peer, in seconds\n" + " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n" + " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n" + " age Duration of connection to the peer, in minutes\n" + " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n" + " peer selection (only displayed if the -asmap config option is set)\n" + " id Peer index, in increasing order of peer connections since node startup\n" + " address IP address and port of the peer\n" + " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n" + "* The connection counts table displays the number of peers by direction, network, and the totals\n" + " for each, as well as a column for block relay peers.\n\n" + "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n" + "Examples:\n\n" + "Connection counts and local addresses only\n" + "> bitcoin-cli -netinfo\n\n" + "Compact peers listing\n" + "> bitcoin-cli -netinfo 1\n\n" + "Full dashboard\n" + "> bitcoin-cli -netinfo 4\n\n" + "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n" + "> watch --interval 1 --no-title bitcoin-cli -netinfo 4\n\n" + "See this help\n" + "> bitcoin-cli -netinfo help\n"}; + } const int64_t m_time_now{GetSystemTimeInSeconds()}; public: @@ -361,6 +419,10 @@ public: uint8_t n{0}; if (ParseUInt8(args.at(0), &n)) { m_details_level = n; + } else if (args.at(0) == "help") { + m_is_help_requested = true; + } else { + throw std::runtime_error(strprintf("invalid -netinfo argument: %s", args.at(0))); } } UniValue result(UniValue::VARR); @@ -371,6 +433,9 @@ public: UniValue ProcessReply(const UniValue& batch_in) override { + if (m_is_help_requested) { + return JSONRPCReplyObj(NetinfoHelp(), NullUniValue, 1); + } const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)}; if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO]; if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; @@ -424,7 +489,7 @@ public: // Report detailed peer connections list sorted by direction and minimum ping time. if (DetailsRequested() && !m_peers.empty()) { std::sort(m_peers.begin(), m_peers.end()); - result += strprintf("Peer connections sorted by direction and min ping\n<-> relay net mping ping send recv txn blk %*s ", m_max_age_length, "age"); + result += strprintf("<-> relay net mping ping send recv txn blk %*s ", m_max_age_length, "age"); if (m_is_asmap_on) result += " asmap "; result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : ""); for (const Peer& peer : m_peers) { diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index e22b3766cf..321d62fe4d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -11,6 +11,7 @@ #include <consensus/consensus.h> #include <core_io.h> #include <key_io.h> +#include <policy/policy.h> #include <policy/rbf.h> #include <primitives/transaction.h> #include <script/script.h> @@ -40,6 +41,7 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman) { SetupHelpOptions(argsman); + argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-create", "Create new, empty TX.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-json", "Select JSON output", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -95,13 +97,16 @@ static int AppInitRawTx(int argc, char* argv[]) fCreateBlank = gArgs.GetBoolArg("-create", false); - if (argc < 2 || HelpRequested(gArgs)) { + if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { // First part of help message is specific to this utility - std::string strUsage = PACKAGE_NAME " bitcoin-tx utility version " + FormatFullVersion() + "\n\n" + - "Usage: bitcoin-tx [options] <hex-tx> [commands] Update hex-encoded bitcoin transaction\n" + - "or: bitcoin-tx [options] -create [commands] Create hex-encoded bitcoin transaction\n" + - "\n"; - strUsage += gArgs.GetHelpMessage(); + std::string strUsage = PACKAGE_NAME " bitcoin-tx utility version " + FormatFullVersion() + "\n"; + if (!gArgs.IsArgSet("-version")) { + strUsage += "\n" + "Usage: bitcoin-tx [options] <hex-tx> [commands] Update hex-encoded bitcoin transaction\n" + "or: bitcoin-tx [options] -create [commands] Create hex-encoded bitcoin transaction\n" + "\n"; + strUsage += gArgs.GetHelpMessage(); + } tfm::format(std::cout, "%s", strUsage); @@ -192,8 +197,9 @@ static CAmount ExtractAndValidateValue(const std::string& strValue) static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) { int64_t newVersion; - if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) + if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > TX_MAX_STANDARD_VERSION) { throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'"); + } tx.nVersion = (int) newVersion; } diff --git a/src/bitcoin-util-res.rc b/src/bitcoin-util-res.rc new file mode 100644 index 0000000000..3f0fa8ab6d --- /dev/null +++ b/src/bitcoin-util-res.rc @@ -0,0 +1,35 @@ +#include <windows.h> // needed for VERSIONINFO +#include "clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "Bitcoin" + VALUE "FileDescription", "bitcoin-util (CLI Bitcoin utility)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoin-util" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "bitcoin-util.exe" + VALUE "ProductName", "bitcoin-util" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoin-util.cpp b/src/bitcoin-util.cpp new file mode 100644 index 0000000000..af07b28d3d --- /dev/null +++ b/src/bitcoin-util.cpp @@ -0,0 +1,221 @@ +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#include <arith_uint256.h> +#include <clientversion.h> +#include <coins.h> +#include <consensus/consensus.h> +#include <core_io.h> +#include <key_io.h> +#include <policy/rbf.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <script/sign.h> +#include <script/signingprovider.h> +#include <univalue.h> +#include <util/moneystr.h> +#include <util/rbf.h> +#include <util/strencodings.h> +#include <util/string.h> +#include <util/system.h> +#include <util/translation.h> + +#include <atomic> +#include <functional> +#include <memory> +#include <stdio.h> +#include <thread> + +#include <boost/algorithm/string.hpp> + +static const int CONTINUE_EXECUTION=-1; + +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + +static void SetupBitcoinUtilArgs(ArgsManager &argsman) +{ + SetupHelpOptions(argsman); + + argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + + SetupChainParamsBaseOptions(argsman); +} + +// This function returns either one of EXIT_ codes when it's expected to stop the process or +// CONTINUE_EXECUTION when it's expected to continue further. +static int AppInitUtil(int argc, char* argv[]) +{ + SetupBitcoinUtilArgs(gArgs); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); + return EXIT_FAILURE; + } + + // Check for chain settings (Params() calls are only valid after this clause) + try { + SelectParams(gArgs.GetChainName()); + } catch (const std::exception& e) { + tfm::format(std::cerr, "Error: %s\n", e.what()); + return EXIT_FAILURE; + } + + if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { + // First part of help message is specific to this utility + std::string strUsage = PACKAGE_NAME " bitcoin-util utility version " + FormatFullVersion() + "\n"; + if (!gArgs.IsArgSet("-version")) { + strUsage += "\n" + "Usage: bitcoin-util [options] [commands] Do stuff\n"; + strUsage += "\n" + gArgs.GetHelpMessage(); + } + + tfm::format(std::cout, "%s", strUsage); + + if (argc < 2) { + tfm::format(std::cerr, "Error: too few parameters\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } + return CONTINUE_EXECUTION; +} + +static void grind_task(uint32_t nBits, CBlockHeader& header_orig, uint32_t offset, uint32_t step, std::atomic<bool>& found) +{ + arith_uint256 target; + bool neg, over; + target.SetCompact(nBits, &neg, &over); + if (target == 0 || neg || over) return; + CBlockHeader header = header_orig; // working copy + header.nNonce = offset; + + uint32_t finish = std::numeric_limits<uint32_t>::max() - step; + finish = finish - (finish % step) + offset; + + while (!found && header.nNonce < finish) { + const uint32_t next = (finish - header.nNonce < 5000*step) ? finish : header.nNonce + 5000*step; + do { + if (UintToArith256(header.GetHash()) <= target) { + if (!found.exchange(true)) { + header_orig.nNonce = header.nNonce; + } + return; + } + header.nNonce += step; + } while(header.nNonce != next); + } +} + +static int Grind(int argc, char* argv[], std::string& strPrint) +{ + if (argc != 1) { + strPrint = "Must specify block header to grind"; + return 1; + } + + CBlockHeader header; + if (!DecodeHexBlockHeader(header, argv[0])) { + strPrint = "Could not decode block header"; + return 1; + } + + uint32_t nBits = header.nBits; + std::atomic<bool> found{false}; + + std::vector<std::thread> threads; + int n_tasks = std::max(1u, std::thread::hardware_concurrency()); + for (int i = 0; i < n_tasks; ++i) { + threads.emplace_back( grind_task, nBits, std::ref(header), i, n_tasks, std::ref(found) ); + } + for (auto& t : threads) { + t.join(); + } + if (!found) { + strPrint = "Could not satisfy difficulty target"; + return 1; + } + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << header; + strPrint = HexStr(ss); + return 0; +} + +static int CommandLineUtil(int argc, char* argv[]) +{ + if (argc <= 1) return 1; + + std::string strPrint; + int nRet = 0; + + try { + while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) { + --argc; + ++argv; + } + + char* command = argv[1]; + if (strcmp(command, "grind") == 0) { + nRet = Grind(argc-2, argv+2, strPrint); + } else { + strPrint = strprintf("Unknown command %s", command); + nRet = 1; + } + } + catch (const std::exception& e) { + strPrint = std::string("error: ") + e.what(); + nRet = EXIT_FAILURE; + } + catch (...) { + PrintExceptionContinue(nullptr, "CommandLineUtil()"); + throw; + } + + if (strPrint != "") { + tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint); + } + return nRet; +} + +#ifdef WIN32 +// Export main() and ensure working ASLR on Windows. +// Exporting a symbol will prevent the linker from stripping +// the .reloc section from the binary, which is a requirement +// for ASLR. This is a temporary workaround until a fixed +// version of binutils is used for releases. +__declspec(dllexport) int main(int argc, char* argv[]) +#else +int main(int argc, char* argv[]) +#endif +{ + SetupEnvironment(); + + try { + int ret = AppInitUtil(argc, argv); + if (ret != CONTINUE_EXECUTION) + return ret; + } + catch (const std::exception& e) { + PrintExceptionContinue(&e, "AppInitUtil()"); + return EXIT_FAILURE; + } catch (...) { + PrintExceptionContinue(nullptr, "AppInitUtil()"); + return EXIT_FAILURE; + } + + int ret = EXIT_FAILURE; + try { + ret = CommandLineUtil(argc, argv); + } + catch (const std::exception& e) { + PrintExceptionContinue(&e, "CommandLineUtil()"); + } catch (...) { + PrintExceptionContinue(nullptr, "CommandLineUtil()"); + } + return ret; +} diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index b9c2fe2d34..3e8e5fc7bc 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -24,14 +24,20 @@ static void SetupWalletToolArgs(ArgsManager& argsman) SetupHelpOptions(argsman); SetupChainParamsBaseOptions(argsman); + argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); + argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS); argsman.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS); + argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); argsman.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); argsman.AddArg("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); + argsman.AddArg("dump", "Print out all of the wallet key-value records", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); + argsman.AddArg("createfromdump", "Create new wallet file from dumped records", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); } static bool WalletAppInit(int argc, char* argv[]) @@ -42,16 +48,18 @@ static bool WalletAppInit(int argc, char* argv[]) tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message); return false; } - if (argc < 2 || HelpRequested(gArgs)) { - std::string usage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" + - "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n" + - "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" + - "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" + - "Usage:\n" + - " bitcoin-wallet [options] <command>\n\n" + - gArgs.GetHelpMessage(); - - tfm::format(std::cout, "%s", usage); + if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { + std::string strUsage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n"; + if (!gArgs.IsArgSet("-version")) { + strUsage += "\n" + "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n" + "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" + "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" + "Usage:\n" + " bitcoin-wallet [options] <command>\n"; + strUsage += "\n" + gArgs.GetHelpMessage(); + } + tfm::format(std::cout, "%s", strUsage); return false; } @@ -112,8 +120,9 @@ int main(int argc, char* argv[]) ECCVerifyHandle globalVerifyHandle; ECC_Start(); - if (!WalletTool::ExecuteWalletToolFunc(method, name)) + if (!WalletTool::ExecuteWalletToolFunc(gArgs, method, name)) { return EXIT_FAILURE; + } ECC_Stop(); return EXIT_SUCCESS; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 455a82e390..b7bcb534ef 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -28,15 +28,6 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = urlDecode; -static void WaitForShutdown(NodeContext& node) -{ - while (!ShutdownRequested()) - { - UninterruptibleSleep(std::chrono::milliseconds{200}); - } - Interrupt(node); -} - static bool AppInit(int argc, char* argv[]) { NodeContext node; @@ -57,11 +48,11 @@ static bool AppInit(int argc, char* argv[]) if (HelpRequested(args) || args.IsArgSet("-version")) { std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n"; - if (args.IsArgSet("-version")) { - strUsage += FormatParagraph(LicenseInfo()) + "\n"; - } else { - strUsage += "\nUsage: bitcoind [options] Start " PACKAGE_NAME "\n"; - strUsage += "\n" + args.GetHelpMessage(); + if (!args.IsArgSet("-version")) { + strUsage += FormatParagraph(LicenseInfo()) + "\n" + "\nUsage: bitcoind [options] Start " PACKAGE_NAME "\n" + "\n"; + strUsage += args.GetHelpMessage(); } tfm::format(std::cout, "%s", strUsage); @@ -147,12 +138,10 @@ static bool AppInit(int argc, char* argv[]) PrintExceptionContinue(nullptr, "AppInit()"); } - if (!fRet) - { - Interrupt(node); - } else { - WaitForShutdown(node); + if (fRet) { + WaitForShutdown(); } + Interrupt(node); Shutdown(node); return fRet; diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index a47709cd82..aa111b5939 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 9a6fb4abd0..41fa0b6fa0 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/bloom.cpp b/src/bloom.cpp index d182f0728e..d0128a26d7 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/bloom.h b/src/bloom.h index 24dc607cd9..fdaa8abfb2 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fedb032db2..88cf5ef0a8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -272,7 +272,7 @@ public: bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"); vSeeds.emplace_back("178.128.221.177"); vSeeds.emplace_back("2a01:7c8:d005:390::5"); - vSeeds.emplace_back("ntv3mtqw5wt63red.onion:38333"); + vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333"); consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000019fd16269a"); consensus.defaultAssumeValid = uint256S("0x0000002a1de0f46379358c1fd09906f7ac59adf3712323ed90eb59e4c183c020"); // 9434 diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 603969aaea..2c517b58f8 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 9b4ae2f7ab..d593cff722 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 The Bitcoin Core developers +// Copyright (c) 2014-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/checkqueue.h b/src/checkqueue.h index e3faa1dec0..4ceeb3600a 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -6,13 +6,12 @@ #define BITCOIN_CHECKQUEUE_H #include <sync.h> +#include <tinyformat.h> +#include <util/threadnames.h> #include <algorithm> #include <vector> -#include <boost/thread/condition_variable.hpp> -#include <boost/thread/mutex.hpp> - template <typename T> class CCheckQueueControl; @@ -31,61 +30,64 @@ class CCheckQueue { private: //! Mutex to protect the inner state - boost::mutex mutex; + Mutex m_mutex; //! Worker threads block on this when out of work - boost::condition_variable condWorker; + std::condition_variable m_worker_cv; //! Master thread blocks on this when out of work - boost::condition_variable condMaster; + std::condition_variable m_master_cv; //! The queue of elements to be processed. //! As the order of booleans doesn't matter, it is used as a LIFO (stack) - std::vector<T> queue; + std::vector<T> queue GUARDED_BY(m_mutex); //! The number of workers (including the master) that are idle. - int nIdle; + int nIdle GUARDED_BY(m_mutex){0}; //! The total number of workers (including the master). - int nTotal; + int nTotal GUARDED_BY(m_mutex){0}; //! The temporary evaluation result. - bool fAllOk; + bool fAllOk GUARDED_BY(m_mutex){true}; /** * Number of verifications that haven't completed yet. * This includes elements that are no longer queued, but still in the * worker's own batches. */ - unsigned int nTodo; + unsigned int nTodo GUARDED_BY(m_mutex){0}; //! The maximum number of elements to be processed in one batch - unsigned int nBatchSize; + const unsigned int nBatchSize; + + std::vector<std::thread> m_worker_threads; + bool m_request_stop GUARDED_BY(m_mutex){false}; /** Internal function that does bulk of the verification work. */ - bool Loop(bool fMaster = false) + bool Loop(bool fMaster) { - boost::condition_variable& cond = fMaster ? condMaster : condWorker; + std::condition_variable& cond = fMaster ? m_master_cv : m_worker_cv; std::vector<T> vChecks; vChecks.reserve(nBatchSize); unsigned int nNow = 0; bool fOk = true; do { { - boost::unique_lock<boost::mutex> lock(mutex); + WAIT_LOCK(m_mutex, lock); // first do the clean-up of the previous loop run (allowing us to do it in the same critsect) if (nNow) { fAllOk &= fOk; nTodo -= nNow; if (nTodo == 0 && !fMaster) // We processed the last element; inform the master it can exit and return the result - condMaster.notify_one(); + m_master_cv.notify_one(); } else { // first iteration nTotal++; } // logically, the do loop starts here - while (queue.empty()) { + while (queue.empty() && !m_request_stop) { if (fMaster && nTodo == 0) { nTotal--; bool fRet = fAllOk; @@ -98,6 +100,10 @@ private: cond.wait(lock); // wait nIdle--; } + if (m_request_stop) { + return false; + } + // Decide how many work units to process now. // * Do not try to do everything at once, but aim for increasingly smaller batches so // all workers finish approximately simultaneously. @@ -106,7 +112,7 @@ private: nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1))); vChecks.resize(nNow); for (unsigned int i = 0; i < nNow; i++) { - // We want the lock on the mutex to be as short as possible, so swap jobs from the global + // We want the lock on the m_mutex to be as short as possible, so swap jobs from the global // queue to the local batch vector instead of copying. vChecks[i].swap(queue.back()); queue.pop_back(); @@ -124,40 +130,68 @@ private: public: //! Mutex to ensure only one concurrent CCheckQueueControl - boost::mutex ControlMutex; + Mutex m_control_mutex; //! Create a new check queue - explicit CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), nBatchSize(nBatchSizeIn) {} + explicit CCheckQueue(unsigned int nBatchSizeIn) + : nBatchSize(nBatchSizeIn) + { + } - //! Worker thread - void Thread() + //! Create a pool of new worker threads. + void StartWorkerThreads(const int threads_num) { - Loop(); + { + LOCK(m_mutex); + nIdle = 0; + nTotal = 0; + fAllOk = true; + } + assert(m_worker_threads.empty()); + for (int n = 0; n < threads_num; ++n) { + m_worker_threads.emplace_back([this, n]() { + util::ThreadRename(strprintf("scriptch.%i", n)); + Loop(false /* worker thread */); + }); + } } //! Wait until execution finishes, and return whether all evaluations were successful. bool Wait() { - return Loop(true); + return Loop(true /* master thread */); } //! Add a batch of checks to the queue void Add(std::vector<T>& vChecks) { - boost::unique_lock<boost::mutex> lock(mutex); + LOCK(m_mutex); for (T& check : vChecks) { queue.push_back(T()); check.swap(queue.back()); } nTodo += vChecks.size(); if (vChecks.size() == 1) - condWorker.notify_one(); + m_worker_cv.notify_one(); else if (vChecks.size() > 1) - condWorker.notify_all(); + m_worker_cv.notify_all(); + } + + //! Stop all of the worker threads. + void StopWorkerThreads() + { + WITH_LOCK(m_mutex, m_request_stop = true); + m_worker_cv.notify_all(); + for (std::thread& t : m_worker_threads) { + t.join(); + } + m_worker_threads.clear(); + WITH_LOCK(m_mutex, m_request_stop = false); } ~CCheckQueue() { + assert(m_worker_threads.empty()); } }; @@ -181,7 +215,7 @@ public: { // passed queue is supposed to be unused, or nullptr if (pqueue != nullptr) { - ENTER_CRITICAL_SECTION(pqueue->ControlMutex); + ENTER_CRITICAL_SECTION(pqueue->m_control_mutex); } } @@ -205,7 +239,7 @@ public: if (!fDone) Wait(); if (pqueue != nullptr) { - LEAVE_CRITICAL_SECTION(pqueue->ControlMutex); + LEAVE_CRITICAL_SECTION(pqueue->m_control_mutex); } } }; diff --git a/src/clientversion.cpp b/src/clientversion.cpp index aaf041602b..29c38e2d3b 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2018 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/clientversion.h b/src/clientversion.h index c925b8f084..2da909f829 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/coins.cpp b/src/coins.cpp index 5de2ed7810..dd84e720e7 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -31,8 +31,6 @@ bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); } -SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} - CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), cachedCoinsUsage(0) {} size_t CCoinsViewCache::DynamicMemoryUsage() const { diff --git a/src/coins.h b/src/coins.h index a3e241ac90..d2eb42d8cf 100644 --- a/src/coins.h +++ b/src/coins.h @@ -8,11 +8,11 @@ #include <compressor.h> #include <core_memusage.h> -#include <crypto/siphash.h> #include <memusage.h> #include <primitives/transaction.h> #include <serialize.h> #include <uint256.h> +#include <util/hasher.h> #include <assert.h> #include <stdint.h> @@ -82,33 +82,6 @@ public: } }; -class SaltedOutpointHasher -{ -private: - /** Salt */ - const uint64_t k0, k1; - -public: - SaltedOutpointHasher(); - - /** - * This *must* return size_t. With Boost 1.46 on 32-bit systems the - * unordered_map will behave unpredictably if the custom hasher returns a - * uint64_t, resulting in failures when syncing the chain (#4634). - * - * Having the hash noexcept allows libstdc++'s unordered_map to recalculate - * the hash during rehash, so it does not have to cache the value. This - * reduces node's memory by sizeof(size_t). The required recalculation has - * a slight performance penalty (around 1.6%), but this is compensated by - * memory savings of about 9% which allow for a larger dbcache setting. - * - * @see https://gcc.gnu.org/onlinedocs/gcc-9.2.0/libstdc++/manual/manual/unordered_associative.html - */ - size_t operator()(const COutPoint& id) const noexcept { - return SipHashUint256Extra(k0, k1, id.hash, id.n); - } -}; - /** * A Coin in one level of the coins database caching hierarchy. * diff --git a/src/compat.h b/src/compat.h index 0be02cae03..dad14748a2 100644 --- a/src/compat.h +++ b/src/compat.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,11 +18,7 @@ #undef FD_SETSIZE // prevent redefinition compiler warning #endif #define FD_SETSIZE 1024 // max number of fds in fd_set - -#include <winsock2.h> // Must be included before mswsock.h and windows.h - -#include <mswsock.h> -#include <windows.h> +#include <winsock2.h> #include <ws2tcpip.h> #include <stdint.h> #else diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h index 4b0b224c69..301c2d914c 100644 --- a/src/compat/assumptions.h +++ b/src/compat/assumptions.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index d17de33e86..8a51f310f7 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -54,6 +54,12 @@ __asm(".symver log2f_old,log2f@GLIBC_2.2.5"); __asm(".symver log2f_old,log2f@GLIBC_2.4"); #elif defined(__aarch64__) __asm(".symver log2f_old,log2f@GLIBC_2.17"); +#elif defined(__powerpc64__) +# ifdef WORDS_BIGENDIAN +__asm(".symver log2f_old,log2f@GLIBC_2.3"); +# else +__asm(".symver log2f_old,log2f@GLIBC_2.17"); +# endif #elif defined(__riscv) __asm(".symver log2f_old,log2f@GLIBC_2.27"); #endif diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index 0367b9a53f..06d0dd6fba 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/consensus/params.h b/src/consensus/params.h index 0983595c6a..217cb019e1 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 9e8e6530f1..f595f16eab 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index e2a9328df8..e78dc9f2a5 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/consensus/validation.h b/src/consensus/validation.h index e007c481df..c4d305434a 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/core_io.h b/src/core_io.h index 80ec80cd50..5469a760ee 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,18 +18,19 @@ class CTransaction; struct CMutableTransaction; class uint256; class UniValue; +class CTxUndo; // core_read.cpp CScript ParseScript(const std::string& s); std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); -NODISCARD bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true); -NODISCARD bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); +[[nodiscard]] bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true); +[[nodiscard]] bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header); /** * Parse a hex string into 256 bits * @param[in] strHex a hex-formatted, 64-character string - * @param[out] result the result of the parasing + * @param[out] result the result of the parsing * @returns true if successful, false if not * * @see ParseHashV for an RPC-oriented version of this @@ -45,6 +46,6 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0); std::string SighashToStr(unsigned char sighash_type); void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); void ScriptToUniv(const CScript& script, UniValue& out, bool include_address); -void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0); +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr); #endif // BITCOIN_CORE_IO_H diff --git a/src/core_read.cpp b/src/core_read.cpp index a2eebbd528..b5fc93886d 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -126,31 +126,72 @@ static bool CheckTxScriptsSanity(const CMutableTransaction& tx) static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data, bool try_no_witness, bool try_witness) { + // General strategy: + // - Decode both with extended serialization (which interprets the 0x0001 tag as a marker for + // the presence of witnesses) and with legacy serialization (which interprets the tag as a + // 0-input 1-output incomplete transaction). + // - Restricted by try_no_witness (which disables legacy if false) and try_witness (which + // disables extended if false). + // - Ignore serializations that do not fully consume the hex string. + // - If neither succeeds, fail. + // - If only one succeeds, return that one. + // - If both decode attempts succeed: + // - If only one passes the CheckTxScriptsSanity check, return that one. + // - If neither or both pass CheckTxScriptsSanity, return the extended one. + + CMutableTransaction tx_extended, tx_legacy; + bool ok_extended = false, ok_legacy = false; + + // Try decoding with extended serialization support, and remember if the result successfully + // consumes the entire input. if (try_witness) { CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION); try { - ssData >> tx; - // If transaction looks sane, we don't try other mode even if requested - if (ssData.empty() && (!try_no_witness || CheckTxScriptsSanity(tx))) { - return true; - } + ssData >> tx_extended; + if (ssData.empty()) ok_extended = true; } catch (const std::exception&) { // Fall through. } } + // Optimization: if extended decoding succeeded and the result passes CheckTxScriptsSanity, + // don't bother decoding the other way. + if (ok_extended && CheckTxScriptsSanity(tx_extended)) { + tx = std::move(tx_extended); + return true; + } + + // Try decoding with legacy serialization, and remember if the result successfully consumes the entire input. if (try_no_witness) { CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); try { - ssData >> tx; - if (ssData.empty()) { - return true; - } + ssData >> tx_legacy; + if (ssData.empty()) ok_legacy = true; } catch (const std::exception&) { // Fall through. } } + // If legacy decoding succeeded and passes CheckTxScriptsSanity, that's our answer, as we know + // at this point that extended decoding either failed or doesn't pass the sanity check. + if (ok_legacy && CheckTxScriptsSanity(tx_legacy)) { + tx = std::move(tx_legacy); + return true; + } + + // If extended decoding succeeded, and neither decoding passes sanity, return the extended one. + if (ok_extended) { + tx = std::move(tx_extended); + return true; + } + + // If legacy decoding succeeded and extended didn't, return the legacy one. + if (ok_legacy) { + tx = std::move(tx_legacy); + return true; + } + + // If none succeeded, we failed. return false; } diff --git a/src/core_write.cpp b/src/core_write.cpp index 3980d8cb2e..a3902863d6 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,7 +11,9 @@ #include <script/standard.h> #include <serialize.h> #include <streams.h> +#include <undo.h> #include <univalue.h> +#include <util/check.h> #include <util/system.h> #include <util/strencodings.h> @@ -177,7 +179,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("addresses", a); } -void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags) +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo) { entry.pushKV("txid", tx.GetHash().GetHex()); entry.pushKV("hash", tx.GetWitnessHash().GetHex()); @@ -189,13 +191,20 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, entry.pushKV("weight", GetTransactionWeight(tx)); entry.pushKV("locktime", (int64_t)tx.nLockTime); - UniValue vin(UniValue::VARR); + UniValue vin{UniValue::VARR}; + + // If available, use Undo data to calculate the fee. Note that txundo == nullptr + // for coinbase transactions and for transactions where undo data is unavailable. + const bool calculate_fee = txundo != nullptr; + CAmount amt_total_in = 0; + CAmount amt_total_out = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxIn& txin = tx.vin[i]; UniValue in(UniValue::VOBJ); - if (tx.IsCoinBase()) + if (tx.IsCoinBase()) { in.pushKV("coinbase", HexStr(txin.scriptSig)); - else { + } else { in.pushKV("txid", txin.prevout.hash.GetHex()); in.pushKV("vout", (int64_t)txin.prevout.n); UniValue o(UniValue::VOBJ); @@ -210,6 +219,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, } in.pushKV("txinwitness", txinwitness); } + if (calculate_fee) { + const CTxOut& prev_txout = txundo->vprevout[i].out; + amt_total_in += prev_txout.nValue; + } in.pushKV("sequence", (int64_t)txin.nSequence); vin.push_back(in); } @@ -228,9 +241,19 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, ScriptPubKeyToUniv(txout.scriptPubKey, o, true); out.pushKV("scriptPubKey", o); vout.push_back(out); + + if (calculate_fee) { + amt_total_out += txout.nValue; + } } entry.pushKV("vout", vout); + if (calculate_fee) { + const CAmount fee = amt_total_in - amt_total_out; + CHECK_NONFATAL(MoneyRange(fee)); + entry.pushKV("fee", ValueFromAmount(fee)); + } + if (!hashBlock.IsNull()) entry.pushKV("blockhash", hashBlock.GetHex()); diff --git a/src/crc32c/.appveyor.yml b/src/crc32c/.appveyor.yml index 7345746750..b23e02e88a 100644 --- a/src/crc32c/.appveyor.yml +++ b/src/crc32c/.appveyor.yml @@ -8,9 +8,9 @@ environment: matrix: # AppVeyor currently has no custom job name feature. # http://help.appveyor.com/discussions/questions/1623-can-i-provide-a-friendly-name-for-jobs - - JOB: Visual Studio 2017 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - CMAKE_GENERATOR: Visual Studio 15 2017 + - JOB: Visual Studio 2019 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + CMAKE_GENERATOR: Visual Studio 16 2019 platform: - x86 @@ -24,10 +24,11 @@ build_script: - git submodule update --init --recursive - mkdir build - cd build - - if "%platform%"=="x64" set CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64 + - if "%platform%"=="x86" (set CMAKE_GENERATOR_PLATFORM="Win32") + else (set CMAKE_GENERATOR_PLATFORM="%platform%") - cmake --version - - cmake .. -G "%CMAKE_GENERATOR%" -DCRC32C_USE_GLOG=0 - -DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%" + - cmake .. -G "%CMAKE_GENERATOR%" -A "%CMAKE_GENERATOR_PLATFORM%" + -DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%" -DCRC32C_USE_GLOG=0 - cmake --build . --config "%CONFIGURATION%" - cd .. diff --git a/src/crc32c/AUTHORS b/src/crc32c/AUTHORS index 6f1f6871a6..ef9b4ea933 100644 --- a/src/crc32c/AUTHORS +++ b/src/crc32c/AUTHORS @@ -7,3 +7,5 @@ Google Inc. Fangming Fang <Fangming.Fang@arm.com> Vadim Skipin <vadim.skipin@gmail.com> +Rodrigo Tobar <rtobar@icrar.org> +Harry Mallon <hjmallon@gmail.com> diff --git a/src/crc32c/CMakeLists.txt b/src/crc32c/CMakeLists.txt index 111a3e3614..71692d5796 100644 --- a/src/crc32c/CMakeLists.txt +++ b/src/crc32c/CMakeLists.txt @@ -5,15 +5,21 @@ cmake_minimum_required(VERSION 3.1) project(Crc32c VERSION 1.1.0 LANGUAGES C CXX) -# This project can use C11, but will gracefully decay down to C89. -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED OFF) -set(CMAKE_C_EXTENSIONS OFF) - -# This project requires C++11. -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +# C standard can be overridden when this is used as a sub-project. +if(NOT CMAKE_C_STANDARD) + # This project can use C11, but will gracefully decay down to C89. + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED OFF) + set(CMAKE_C_EXTENSIONS OFF) +endif(NOT CMAKE_C_STANDARD) + +# C++ standard can be overridden when this is used as a sub-project. +if(NOT CMAKE_CXX_STANDARD) + # This project requires C++11. + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) +endif(NOT CMAKE_CXX_STANDARD) # https://github.com/izenecloud/cmake/blob/master/SetCompilerWarningAll.cmake if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") @@ -269,7 +275,7 @@ target_sources(crc32c PRIVATE "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" "src/crc32c_arm64.h" - "src/crc32c_arm64_linux_check.h" + "src/crc32c_arm64_check.h" "src/crc32c_internal.h" "src/crc32c_portable.cc" "src/crc32c_prefetch.h" @@ -405,19 +411,24 @@ if(CRC32C_INSTALL) ) include(CMakePackageConfigHelpers) + configure_package_config_file( + "${PROJECT_NAME}Config.cmake.in" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) write_basic_package_version_file( - "${PROJECT_BINARY_DIR}/Crc32cConfigVersion.cmake" - COMPATIBILITY SameMajorVersion + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + COMPATIBILITY SameMajorVersion ) install( EXPORT Crc32cTargets NAMESPACE Crc32c:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crc32c" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) install( FILES - "Crc32cConfig.cmake" - "${PROJECT_BINARY_DIR}/Crc32cConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crc32c" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) endif(CRC32C_INSTALL) diff --git a/src/crc32c/Crc32cConfig.cmake b/src/crc32c/Crc32cConfig.cmake.in index 4d6057ec26..c6b8fc7913 100644 --- a/src/crc32c/Crc32cConfig.cmake +++ b/src/crc32c/Crc32cConfig.cmake.in @@ -2,4 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. See the AUTHORS file for names of contributors. +@PACKAGE_INIT@ + include("${CMAKE_CURRENT_LIST_DIR}/Crc32cTargets.cmake") + +check_required_components(Crc32c) diff --git a/src/crc32c/src/crc32c.cc b/src/crc32c/src/crc32c.cc index 4d3018af47..804133bc17 100644 --- a/src/crc32c/src/crc32c.cc +++ b/src/crc32c/src/crc32c.cc @@ -8,7 +8,7 @@ #include <cstdint> #include "./crc32c_arm64.h" -#include "./crc32c_arm64_linux_check.h" +#include "./crc32c_arm64_check.h" #include "./crc32c_internal.h" #include "./crc32c_sse42.h" #include "./crc32c_sse42_check.h" @@ -20,8 +20,8 @@ uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) { static bool can_use_sse42 = CanUseSse42(); if (can_use_sse42) return ExtendSse42(crc, data, count); #elif HAVE_ARM64_CRC32C - static bool can_use_arm_linux = CanUseArm64Linux(); - if (can_use_arm_linux) return ExtendArm64(crc, data, count); + static bool can_use_arm64_crc32 = CanUseArm64Crc32(); + if (can_use_arm64_crc32) return ExtendArm64(crc, data, count); #endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) return ExtendPortable(crc, data, count); diff --git a/src/crc32c/src/crc32c_arm64.cc b/src/crc32c/src/crc32c_arm64.cc index b872245f95..1da04ed34a 100644 --- a/src/crc32c/src/crc32c_arm64.cc +++ b/src/crc32c/src/crc32c_arm64.cc @@ -64,7 +64,7 @@ namespace crc32c { -uint32_t ExtendArm64(uint32_t crc, const uint8_t *buf, size_t size) { +uint32_t ExtendArm64(uint32_t crc, const uint8_t *data, size_t size) { int64_t length = size; uint32_t crc0, crc1, crc2, crc3; uint64_t t0, t1, t2; @@ -74,7 +74,6 @@ uint32_t ExtendArm64(uint32_t crc, const uint8_t *buf, size_t size) { const poly64_t k0 = 0x8d96551c, k1 = 0xbd6f81f8, k2 = 0xdcb17aa4; crc = crc ^ kCRC32Xor; - const uint8_t *p = reinterpret_cast<const uint8_t *>(buf); while (length >= KBYTES) { crc0 = crc; @@ -83,14 +82,14 @@ uint32_t ExtendArm64(uint32_t crc, const uint8_t *buf, size_t size) { crc3 = 0; // Process 1024 bytes in parallel. - CRC32C1024BYTES(p); + CRC32C1024BYTES(data); // Merge the 4 partial CRC32C values. t2 = (uint64_t)vmull_p64(crc2, k2); t1 = (uint64_t)vmull_p64(crc1, k1); t0 = (uint64_t)vmull_p64(crc0, k0); - crc = __crc32cd(crc3, *(uint64_t *)p); - p += sizeof(uint64_t); + crc = __crc32cd(crc3, *(uint64_t *)data); + data += sizeof(uint64_t); crc ^= __crc32cd(0, t2); crc ^= __crc32cd(0, t1); crc ^= __crc32cd(0, t0); @@ -99,23 +98,23 @@ uint32_t ExtendArm64(uint32_t crc, const uint8_t *buf, size_t size) { } while (length >= 8) { - crc = __crc32cd(crc, *(uint64_t *)p); - p += 8; + crc = __crc32cd(crc, *(uint64_t *)data); + data += 8; length -= 8; } if (length & 4) { - crc = __crc32cw(crc, *(uint32_t *)p); - p += 4; + crc = __crc32cw(crc, *(uint32_t *)data); + data += 4; } if (length & 2) { - crc = __crc32ch(crc, *(uint16_t *)p); - p += 2; + crc = __crc32ch(crc, *(uint16_t *)data); + data += 2; } if (length & 1) { - crc = __crc32cb(crc, *p); + crc = __crc32cb(crc, *data); } return crc ^ kCRC32Xor; diff --git a/src/crc32c/src/crc32c_arm64.h b/src/crc32c/src/crc32c_arm64.h index 100cd56ec8..e093687ddc 100644 --- a/src/crc32c/src/crc32c_arm64.h +++ b/src/crc32c/src/crc32c_arm64.h @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -// Linux-specific code checking the availability for ARM CRC32C instructions. +// ARM-specific code -#ifndef CRC32C_CRC32C_ARM_LINUX_H_ -#define CRC32C_CRC32C_ARM_LINUX_H_ +#ifndef CRC32C_CRC32C_ARM_H_ +#define CRC32C_CRC32C_ARM_H_ #include <cstddef> #include <cstdint> @@ -24,4 +24,4 @@ uint32_t ExtendArm64(uint32_t crc, const uint8_t* data, size_t count); #endif // HAVE_ARM64_CRC32C -#endif // CRC32C_CRC32C_ARM_LINUX_H_ +#endif // CRC32C_CRC32C_ARM_H_ diff --git a/src/crc32c/src/crc32c_arm64_linux_check.h b/src/crc32c/src/crc32c_arm64_check.h index 1a20a757bb..62a07aba09 100644 --- a/src/crc32c/src/crc32c_arm64_linux_check.h +++ b/src/crc32c/src/crc32c_arm64_check.h @@ -2,12 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -// ARM Linux-specific code checking for the availability of CRC32C instructions. +// ARM-specific code checking for the availability of CRC32C instructions. -#ifndef CRC32C_CRC32C_ARM_LINUX_CHECK_H_ -#define CRC32C_CRC32C_ARM_LINUX_CHECK_H_ - -// X86-specific code checking for the availability of SSE4.2 instructions. +#ifndef CRC32C_CRC32C_ARM_CHECK_H_ +#define CRC32C_CRC32C_ARM_CHECK_H_ #include <cstddef> #include <cstdint> @@ -18,6 +16,7 @@ #if HAVE_ARM64_CRC32C +#ifdef __linux__ #if HAVE_STRONG_GETAUXVAL #include <sys/auxv.h> #elif HAVE_WEAK_GETAUXVAL @@ -27,17 +26,28 @@ extern "C" unsigned long getauxval(unsigned long type) __attribute__((weak)); #define AT_HWCAP 16 #endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL +#endif // defined (__linux__) + +#ifdef __APPLE__ +#include <sys/types.h> +#include <sys/sysctl.h> +#endif // defined (__APPLE__) namespace crc32c { -inline bool CanUseArm64Linux() { -#if HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL +inline bool CanUseArm64Crc32() { +#if defined (__linux__) && (HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL) // From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code. constexpr unsigned long kHWCAP_PMULL = 1 << 4; constexpr unsigned long kHWCAP_CRC32 = 1 << 7; unsigned long hwcap = (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0; return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) == (kHWCAP_PMULL | kHWCAP_CRC32); +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + return sysctlbyname("hw.optional.armv8_crc32", &val, &len, nullptr, 0) == 0 + && val != 0; #else return false; #endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL @@ -47,4 +57,4 @@ inline bool CanUseArm64Linux() { #endif // HAVE_ARM64_CRC32C -#endif // CRC32C_CRC32C_ARM_LINUX_CHECK_H_ +#endif // CRC32C_CRC32C_ARM_CHECK_H_ diff --git a/src/crc32c/src/crc32c_benchmark.cc b/src/crc32c/src/crc32c_benchmark.cc index c464304b3f..51194b370a 100644 --- a/src/crc32c/src/crc32c_benchmark.cc +++ b/src/crc32c/src/crc32c_benchmark.cc @@ -16,7 +16,7 @@ #endif // CRC32C_TESTS_BUILT_WITH_GLOG #include "./crc32c_arm64.h" -#include "./crc32c_arm64_linux_check.h" +#include "./crc32c_arm64_check.h" #include "./crc32c_internal.h" #include "./crc32c_sse42.h" #include "./crc32c_sse42_check.h" @@ -58,8 +58,8 @@ BENCHMARK_REGISTER_F(CRC32CBenchmark, Portable) #if HAVE_ARM64_CRC32C -BENCHMARK_DEFINE_F(CRC32CBenchmark, ArmLinux)(benchmark::State& state) { - if (!crc32c::CanUseArm64Linux()) { +BENCHMARK_DEFINE_F(CRC32CBenchmark, ArmCRC32C)(benchmark::State& state) { + if (!crc32c::CanUseArm64Crc32()) { state.SkipWithError("ARM CRC32C instructions not available or not enabled"); return; } @@ -69,7 +69,7 @@ BENCHMARK_DEFINE_F(CRC32CBenchmark, ArmLinux)(benchmark::State& state) { crc = crc32c::ExtendArm64(crc, block_buffer_, block_size_); state.SetBytesProcessed(state.iterations() * block_size_); } -BENCHMARK_REGISTER_F(CRC32CBenchmark, ArmLinux) +BENCHMARK_REGISTER_F(CRC32CBenchmark, ArmCRC32C) ->RangeMultiplier(16) ->Range(256, 16777216); // Block size. diff --git a/src/crc32c/src/crc32c_read_le.h b/src/crc32c/src/crc32c_read_le.h index 3bd45fe3aa..673a2a0db7 100644 --- a/src/crc32c/src/crc32c_read_le.h +++ b/src/crc32c/src/crc32c_read_le.h @@ -32,14 +32,14 @@ inline uint32_t ReadUint32LE(const uint8_t* buffer) { // Reads a little-endian 64-bit integer from a 64-bit-aligned buffer. inline uint64_t ReadUint64LE(const uint8_t* buffer) { #if BYTE_ORDER_BIG_ENDIAN - return ((static_cast<uint32_t>(static_cast<uint8_t>(buffer[0]))) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[1])) << 8) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[2])) << 16) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[3])) << 24) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[4])) << 32) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[5])) << 40) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[6])) << 48) | - (static_cast<uint32_t>(static_cast<uint8_t>(buffer[7])) << 56)); + return ((static_cast<uint64_t>(static_cast<uint8_t>(buffer[0]))) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[1])) << 8) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[2])) << 16) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[3])) << 24) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[4])) << 32) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[5])) << 40) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[6])) << 48) | + (static_cast<uint64_t>(static_cast<uint8_t>(buffer[7])) << 56)); #else // !BYTE_ORDER_BIG_ENDIAN uint64_t result; // This should be optimized to a single instruction. diff --git a/src/crypto/chacha_poly_aead.h b/src/crypto/chacha_poly_aead.h index b3ba781cdd..0afe8fcc14 100644 --- a/src/crypto/chacha_poly_aead.h +++ b/src/crypto/chacha_poly_aead.h @@ -17,12 +17,12 @@ static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/ /* A AEAD class for ChaCha20-Poly1305@bitcoin. * * ChaCha20 is a stream cipher designed by Daniel Bernstein and described in - * <ref>[http://cr.yp.to/chacha/chacha-20080128.pdf ChaCha20]</ref>. It operates + * <ref>[https://cr.yp.to/chacha/chacha-20080128.pdf ChaCha20]</ref>. It operates * by permuting 128 fixed bits, 128 or 256 bits of key, a 64 bit nonce and a 64 * bit counter into 64 bytes of output. This output is used as a keystream, with * any unused bytes simply discarded. * - * Poly1305 <ref>[http://cr.yp.to/mac/poly1305-20050329.pdf Poly1305]</ref>, also + * Poly1305 <ref>[https://cr.yp.to/mac/poly1305-20050329.pdf Poly1305]</ref>, also * by Daniel Bernstein, is a one-time Carter-Wegman MAC that computes a 128 bit * integrity tag given a message and a single-use 256 bit secret key. * diff --git a/src/crypto/common.h b/src/crypto/common.h index c1acf8b22e..dc12ed9942 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 The Bitcoin Core developers +// Copyright (c) 2014-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp new file mode 100644 index 0000000000..fbd14f9325 --- /dev/null +++ b/src/crypto/muhash.cpp @@ -0,0 +1,338 @@ +// Copyright (c) 2017-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <crypto/muhash.h> + +#include <crypto/chacha20.h> +#include <crypto/common.h> +#include <hash.h> + +#include <cassert> +#include <cstdio> +#include <limits> + +namespace { + +using limb_t = Num3072::limb_t; +using double_limb_t = Num3072::double_limb_t; +constexpr int LIMB_SIZE = Num3072::LIMB_SIZE; +constexpr int LIMBS = Num3072::LIMBS; +/** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */ +constexpr limb_t MAX_PRIME_DIFF = 1103717; + +/** Extract the lowest limb of [c0,c1,c2] into n, and left shift the number by 1 limb. */ +inline void extract3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& n) +{ + n = c0; + c0 = c1; + c1 = c2; + c2 = 0; +} + +/** [c0,c1] = a * b */ +inline void mul(limb_t& c0, limb_t& c1, const limb_t& a, const limb_t& b) +{ + double_limb_t t = (double_limb_t)a * b; + c1 = t >> LIMB_SIZE; + c0 = t; +} + +/* [c0,c1,c2] += n * [d0,d1,d2]. c2 is 0 initially */ +inline void mulnadd3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& d0, limb_t& d1, limb_t& d2, const limb_t& n) +{ + double_limb_t t = (double_limb_t)d0 * n + c0; + c0 = t; + t >>= LIMB_SIZE; + t += (double_limb_t)d1 * n + c1; + c1 = t; + t >>= LIMB_SIZE; + c2 = t + d2 * n; +} + +/* [c0,c1] *= n */ +inline void muln2(limb_t& c0, limb_t& c1, const limb_t& n) +{ + double_limb_t t = (double_limb_t)c0 * n; + c0 = t; + t >>= LIMB_SIZE; + t += (double_limb_t)c1 * n; + c1 = t; +} + +/** [c0,c1,c2] += a * b */ +inline void muladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b) +{ + double_limb_t t = (double_limb_t)a * b; + limb_t th = t >> LIMB_SIZE; + limb_t tl = t; + + c0 += tl; + th += (c0 < tl) ? 1 : 0; + c1 += th; + c2 += (c1 < th) ? 1 : 0; +} + +/** [c0,c1,c2] += 2 * a * b */ +inline void muldbladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b) +{ + double_limb_t t = (double_limb_t)a * b; + limb_t th = t >> LIMB_SIZE; + limb_t tl = t; + + c0 += tl; + limb_t tt = th + ((c0 < tl) ? 1 : 0); + c1 += tt; + c2 += (c1 < tt) ? 1 : 0; + c0 += tl; + th += (c0 < tl) ? 1 : 0; + c1 += th; + c2 += (c1 < th) ? 1 : 0; +} + +/** + * Add limb a to [c0,c1]: [c0,c1] += a. Then extract the lowest + * limb of [c0,c1] into n, and left shift the number by 1 limb. + * */ +inline void addnextract2(limb_t& c0, limb_t& c1, const limb_t& a, limb_t& n) +{ + limb_t c2 = 0; + + // add + c0 += a; + if (c0 < a) { + c1 += 1; + + // Handle case when c1 has overflown + if (c1 == 0) + c2 = 1; + } + + // extract + n = c0; + c0 = c1; + c1 = c2; +} + +/** in_out = in_out^(2^sq) * mul */ +inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul) +{ + for (int j = 0; j < sq; ++j) in_out.Square(); + in_out.Multiply(mul); +} + +} // namespace + +/** Indicates wether d is larger than the modulus. */ +bool Num3072::IsOverflow() const +{ + if (this->limbs[0] <= std::numeric_limits<limb_t>::max() - MAX_PRIME_DIFF) return false; + for (int i = 1; i < LIMBS; ++i) { + if (this->limbs[i] != std::numeric_limits<limb_t>::max()) return false; + } + return true; +} + +void Num3072::FullReduce() +{ + limb_t c0 = MAX_PRIME_DIFF; + limb_t c1 = 0; + for (int i = 0; i < LIMBS; ++i) { + addnextract2(c0, c1, this->limbs[i], this->limbs[i]); + } +} + +Num3072 Num3072::GetInverse() const +{ + // For fast exponentiation a sliding window exponentiation with repunit + // precomputation is utilized. See "Fast Point Decompression for Standard + // Elliptic Curves" (Brumley, Järvinen, 2008). + + Num3072 p[12]; // p[i] = a^(2^(2^i)-1) + Num3072 out; + + p[0] = *this; + + for (int i = 0; i < 11; ++i) { + p[i + 1] = p[i]; + for (int j = 0; j < (1 << i); ++j) p[i + 1].Square(); + p[i + 1].Multiply(p[i]); + } + + out = p[11]; + + square_n_mul(out, 512, p[9]); + square_n_mul(out, 256, p[8]); + square_n_mul(out, 128, p[7]); + square_n_mul(out, 64, p[6]); + square_n_mul(out, 32, p[5]); + square_n_mul(out, 8, p[3]); + square_n_mul(out, 2, p[1]); + square_n_mul(out, 1, p[0]); + square_n_mul(out, 5, p[2]); + square_n_mul(out, 3, p[0]); + square_n_mul(out, 2, p[0]); + square_n_mul(out, 4, p[0]); + square_n_mul(out, 4, p[1]); + square_n_mul(out, 3, p[0]); + + return out; +} + +void Num3072::Multiply(const Num3072& a) +{ + limb_t c0 = 0, c1 = 0, c2 = 0; + Num3072 tmp; + + /* Compute limbs 0..N-2 of this*a into tmp, including one reduction. */ + for (int j = 0; j < LIMBS - 1; ++j) { + limb_t d0 = 0, d1 = 0, d2 = 0; + mul(d0, d1, this->limbs[1 + j], a.limbs[LIMBS + j - (1 + j)]); + for (int i = 2 + j; i < LIMBS; ++i) muladd3(d0, d1, d2, this->limbs[i], a.limbs[LIMBS + j - i]); + mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF); + for (int i = 0; i < j + 1; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[j - i]); + extract3(c0, c1, c2, tmp.limbs[j]); + } + + /* Compute limb N-1 of a*b into tmp. */ + assert(c2 == 0); + for (int i = 0; i < LIMBS; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[LIMBS - 1 - i]); + extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]); + + /* Perform a second reduction. */ + muln2(c0, c1, MAX_PRIME_DIFF); + for (int j = 0; j < LIMBS; ++j) { + addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]); + } + + assert(c1 == 0); + assert(c0 == 0 || c0 == 1); + + /* Perform up to two more reductions if the internal state has already + * overflown the MAX of Num3072 or if it is larger than the modulus or + * if both are the case. + * */ + if (this->IsOverflow()) this->FullReduce(); + if (c0) this->FullReduce(); +} + +void Num3072::Square() +{ + limb_t c0 = 0, c1 = 0, c2 = 0; + Num3072 tmp; + + /* Compute limbs 0..N-2 of this*this into tmp, including one reduction. */ + for (int j = 0; j < LIMBS - 1; ++j) { + limb_t d0 = 0, d1 = 0, d2 = 0; + for (int i = 0; i < (LIMBS - 1 - j) / 2; ++i) muldbladd3(d0, d1, d2, this->limbs[i + j + 1], this->limbs[LIMBS - 1 - i]); + if ((j + 1) & 1) muladd3(d0, d1, d2, this->limbs[(LIMBS - 1 - j) / 2 + j + 1], this->limbs[LIMBS - 1 - (LIMBS - 1 - j) / 2]); + mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF); + for (int i = 0; i < (j + 1) / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[j - i]); + if ((j + 1) & 1) muladd3(c0, c1, c2, this->limbs[(j + 1) / 2], this->limbs[j - (j + 1) / 2]); + extract3(c0, c1, c2, tmp.limbs[j]); + } + + assert(c2 == 0); + for (int i = 0; i < LIMBS / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[LIMBS - 1 - i]); + extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]); + + /* Perform a second reduction. */ + muln2(c0, c1, MAX_PRIME_DIFF); + for (int j = 0; j < LIMBS; ++j) { + addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]); + } + + assert(c1 == 0); + assert(c0 == 0 || c0 == 1); + + /* Perform up to two more reductions if the internal state has already + * overflown the MAX of Num3072 or if it is larger than the modulus or + * if both are the case. + * */ + if (this->IsOverflow()) this->FullReduce(); + if (c0) this->FullReduce(); +} + +void Num3072::SetToOne() +{ + this->limbs[0] = 1; + for (int i = 1; i < LIMBS; ++i) this->limbs[i] = 0; +} + +void Num3072::Divide(const Num3072& a) +{ + if (this->IsOverflow()) this->FullReduce(); + + Num3072 inv{}; + if (a.IsOverflow()) { + Num3072 b = a; + b.FullReduce(); + inv = b.GetInverse(); + } else { + inv = a.GetInverse(); + } + + this->Multiply(inv); + if (this->IsOverflow()) this->FullReduce(); +} + +Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) { + Num3072 out{}; + uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256(); + unsigned char tmp[BYTE_SIZE]; + ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, BYTE_SIZE); + for (int i = 0; i < LIMBS; ++i) { + if (sizeof(limb_t) == 4) { + out.limbs[i] = ReadLE32(tmp + 4 * i); + } else if (sizeof(limb_t) == 8) { + out.limbs[i] = ReadLE64(tmp + 8 * i); + } + } + return out; +} + +MuHash3072::MuHash3072(Span<const unsigned char> in) noexcept +{ + m_numerator = ToNum3072(in); +} + +void MuHash3072::Finalize(uint256& out) noexcept +{ + m_numerator.Divide(m_denominator); + m_denominator.SetToOne(); // Needed to keep the MuHash object valid + + unsigned char data[384]; + for (int i = 0; i < LIMBS; ++i) { + if (sizeof(limb_t) == 4) { + WriteLE32(data + i * 4, m_numerator.limbs[i]); + } else if (sizeof(limb_t) == 8) { + WriteLE64(data + i * 8, m_numerator.limbs[i]); + } + } + + out = (CHashWriter(SER_DISK, 0) << data).GetSHA256(); +} + +MuHash3072& MuHash3072::operator*=(const MuHash3072& mul) noexcept +{ + m_numerator.Multiply(mul.m_numerator); + m_denominator.Multiply(mul.m_denominator); + return *this; +} + +MuHash3072& MuHash3072::operator/=(const MuHash3072& div) noexcept +{ + m_numerator.Multiply(div.m_denominator); + m_denominator.Multiply(div.m_numerator); + return *this; +} + +MuHash3072& MuHash3072::Insert(Span<const unsigned char> in) noexcept { + m_numerator.Multiply(ToNum3072(in)); + return *this; +} + +MuHash3072& MuHash3072::Remove(Span<const unsigned char> in) noexcept { + m_numerator.Divide(ToNum3072(in)); + return *this; +} diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h new file mode 100644 index 0000000000..0c710007c4 --- /dev/null +++ b/src/crypto/muhash.h @@ -0,0 +1,130 @@ +// Copyright (c) 2017-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_MUHASH_H +#define BITCOIN_CRYPTO_MUHASH_H + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#include <serialize.h> +#include <uint256.h> + +#include <stdint.h> + +class Num3072 +{ +private: + void FullReduce(); + bool IsOverflow() const; + Num3072 GetInverse() const; + +public: + +#ifdef HAVE___INT128 + typedef unsigned __int128 double_limb_t; + typedef uint64_t limb_t; + static constexpr int LIMBS = 48; + static constexpr int LIMB_SIZE = 64; +#else + typedef uint64_t double_limb_t; + typedef uint32_t limb_t; + static constexpr int LIMBS = 96; + static constexpr int LIMB_SIZE = 32; +#endif + limb_t limbs[LIMBS]; + + // Sanity check for Num3072 constants + static_assert(LIMB_SIZE * LIMBS == 3072, "Num3072 isn't 3072 bits"); + static_assert(sizeof(double_limb_t) == sizeof(limb_t) * 2, "bad size for double_limb_t"); + static_assert(sizeof(limb_t) * 8 == LIMB_SIZE, "LIMB_SIZE is incorrect"); + + // Hard coded values in MuHash3072 constructor and Finalize + static_assert(sizeof(limb_t) == 4 || sizeof(limb_t) == 8, "bad size for limb_t"); + + void Multiply(const Num3072& a); + void Divide(const Num3072& a); + void SetToOne(); + void Square(); + + Num3072() { this->SetToOne(); }; + + SERIALIZE_METHODS(Num3072, obj) + { + for (auto& limb : obj.limbs) { + READWRITE(limb); + } + } +}; + +/** A class representing MuHash sets + * + * MuHash is a hashing algorithm that supports adding set elements in any + * order but also deleting in any order. As a result, it can maintain a + * running sum for a set of data as a whole, and add/remove when data + * is added to or removed from it. A downside of MuHash is that computing + * an inverse is relatively expensive. This is solved by representing + * the running value as a fraction, and multiplying added elements into + * the numerator and removed elements into the denominator. Only when the + * final hash is desired, a single modular inverse and multiplication is + * needed to combine the two. The combination is also run on serialization + * to allow for space-efficient storage on disk. + * + * As the update operations are also associative, H(a)+H(b)+H(c)+H(d) can + * in fact be computed as (H(a)+H(b)) + (H(c)+H(d)). This implies that + * all of this is perfectly parallellizable: each thread can process an + * arbitrary subset of the update operations, allowing them to be + * efficiently combined later. + * + * Muhash does not support checking if an element is already part of the + * set. That is why this class does not enforce the use of a set as the + * data it represents because there is no efficient way to do so. + * It is possible to add elements more than once and also to remove + * elements that have not been added before. However, this implementation + * is intended to represent a set of elements. + * + * See also https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html. + */ +class MuHash3072 +{ +private: + static constexpr size_t BYTE_SIZE = 384; + + Num3072 m_numerator; + Num3072 m_denominator; + + Num3072 ToNum3072(Span<const unsigned char> in); + +public: + /* The empty set. */ + MuHash3072() noexcept {}; + + /* A singleton with variable sized data in it. */ + explicit MuHash3072(Span<const unsigned char> in) noexcept; + + /* Insert a single piece of data into the set. */ + MuHash3072& Insert(Span<const unsigned char> in) noexcept; + + /* Remove a single piece of data from the set. */ + MuHash3072& Remove(Span<const unsigned char> in) noexcept; + + /* Multiply (resulting in a hash for the union of the sets) */ + MuHash3072& operator*=(const MuHash3072& mul) noexcept; + + /* Divide (resulting in a hash for the difference of the sets) */ + MuHash3072& operator/=(const MuHash3072& div) noexcept; + + /* Finalize into a 32-byte hash. Does not change this object's value. */ + void Finalize(uint256& out) noexcept; + + SERIALIZE_METHODS(MuHash3072, obj) + { + READWRITE(obj.m_numerator); + READWRITE(obj.m_denominator); + } +}; + +#endif // BITCOIN_CRYPTO_MUHASH_H diff --git a/src/crypto/sha256_shani.cpp b/src/crypto/sha256_shani.cpp index 3473f6e39f..4f4d5b5837 100644 --- a/src/crypto/sha256_shani.cpp +++ b/src/crypto/sha256_shani.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // diff --git a/src/crypto/sha256_sse4.cpp b/src/crypto/sha256_sse4.cpp index 89f529a3ab..143752c7cf 100644 --- a/src/crypto/sha256_sse4.cpp +++ b/src/crypto/sha256_sse4.cpp @@ -1001,7 +1001,7 @@ void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks) ; This code is described in an Intel White-Paper: ; "Fast SHA-256 Implementations on Intel Architecture Processors" ; -; To find it, surf to http://www.intel.com/p/en_US/embedded +; To find it, surf to https://www.intel.com/p/en_US/embedded ; and search for that title. ; The paper is expected to be released roughly at the end of April, 2012 ; diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp index 2e0106b165..2e90c393e1 100644 --- a/src/crypto/siphash.cpp +++ b/src/crypto/siphash.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018 The Bitcoin Core developers +// 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. diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h index 6b38950f8e..b573526932 100644 --- a/src/crypto/siphash.h +++ b/src/crypto/siphash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018 The Bitcoin Core developers +// 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. diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 2daf676c4a..1166466771 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -225,7 +225,7 @@ private: * [0, 1) and simply multiply it by the size. Then we just shift the result down by * 32-bits to get our bucket number. The result has non-uniformity the same as a * mod, but it is much faster to compute. More about this technique can be found at - * http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ . + * https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ . * * The resulting non-uniformity is also more equally distributed which would be * advantageous for something like linear probing, though it shouldn't matter diff --git a/src/dbwrapper.h b/src/dbwrapper.h index d58d709e7e..c119036db2 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/flatfile.cpp b/src/flatfile.cpp index 8a8f7b681c..11cf357f3d 100644 --- a/src/flatfile.cpp +++ b/src/flatfile.cpp @@ -92,6 +92,7 @@ bool FlatFileSeq::Flush(const FlatFilePos& pos, bool finalize) fclose(file); return error("%s: failed to commit file %d", __func__, pos.nFile); } + DirectoryCommit(m_dir); fclose(file); return true; diff --git a/src/fs.cpp b/src/fs.cpp index eef9c81de9..4f20ca4d28 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -31,6 +31,12 @@ FILE *fopen(const fs::path& p, const char *mode) #endif } +fs::path AbsPathJoin(const fs::path& base, const fs::path& path) +{ + assert(base.is_absolute()); + return fs::absolute(path, base); +} + #ifndef WIN32 static std::string GetErrorReason() @@ -21,6 +21,17 @@ namespace fs = boost::filesystem; namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode); + /** + * Helper function for joining two paths + * + * @param[in] base Base path + * @param[in] path Path to combine with base + * @returns path unchanged if it is an absolute path, otherwise returns base joined with path. Returns base unchanged if path is empty. + * @pre Base path must be absolute + * @post Returned path will always be absolute + */ + fs::path AbsPathJoin(const fs::path& base, const fs::path& path); + class FileLock { public: diff --git a/src/hash.cpp b/src/hash.cpp index 3657b38639..cc46043c2b 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2018 The Bitcoin Core developers +// Copyright (c) 2013-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. @@ -15,7 +15,7 @@ inline uint32_t ROTL32(uint32_t x, int8_t r) unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vDataToHash) { - // The following is MurmurHash3 (x86_32), see http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + // The following is MurmurHash3 (x86_32), see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp uint32_t h1 = nHashSeed; const uint32_t c1 = 0xcc9e2d51; const uint32_t c2 = 0x1b873593; diff --git a/src/hash.h b/src/hash.h index 6d876076ee..1456a899d8 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -197,7 +197,7 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL } /** Single-SHA256 a 32-byte input (represented as uint256). */ -NODISCARD uint256 SHA256Uint256(const uint256& input); +[[nodiscard]] uint256 SHA256Uint256(const uint256& input); unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vDataToHash); diff --git a/src/httprpc.cpp b/src/httprpc.cpp index f1b9997371..cb8b220895 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -68,6 +68,8 @@ private: static std::string strRPCUserColonPass; /* Stored RPC timer interface (for unregistration) */ static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface; +/* List of -rpcauth values */ +static std::vector<std::vector<std::string>> g_rpcauth; /* RPC Auth Whitelist */ static std::map<std::string, std::set<std::string>> g_rpc_whitelist; static bool g_rpc_whitelist_default = false; @@ -99,15 +101,7 @@ static bool multiUserAuthorized(std::string strUserPass) std::string strUser = strUserPass.substr(0, strUserPass.find(':')); std::string strPass = strUserPass.substr(strUserPass.find(':') + 1); - for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) { - //Search for multi-user login/pass "rpcauth" from config - std::vector<std::string> vFields; - boost::split(vFields, strRPCAuth, boost::is_any_of(":$")); - if (vFields.size() != 3) { - //Incorrect formatting in config file - continue; - } - + for (const auto& vFields : g_rpcauth) { std::string strName = vFields[0]; if (!TimingResistantEqual(strName, strUser)) { continue; @@ -259,6 +253,16 @@ static bool InitRPCAuthentication() if (gArgs.GetArg("-rpcauth","") != "") { LogPrintf("Using rpcauth authentication.\n"); + for (const std::string& rpcauth : gArgs.GetArgs("-rpcauth")) { + std::vector<std::string> fields; + boost::split(fields, rpcauth, boost::is_any_of(":$")); + if (fields.size() == 3) { + g_rpcauth.push_back(fields); + } else { + LogPrintf("Invalid -rpcauth argument.\n"); + return false; + } + } } g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist")); diff --git a/src/httprpc.h b/src/httprpc.h index a6a38fc95a..97af6f7bb1 100644 --- a/src/httprpc.h +++ b/src/httprpc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 1e5ea2de83..0a8e58ab67 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -33,13 +33,6 @@ #include <support/events.h> -#ifdef EVENT__HAVE_NETINET_IN_H -#include <netinet/in.h> -#ifdef _XOPEN_SOURCE_EXTENDED -#include <arpa/inet.h> -#endif -#endif - /** Maximum size of http request (request line + headers) */ static const size_t MAX_HEADERS_SIZE = 8192; diff --git a/src/index/base.cpp b/src/index/base.cpp index e67b813763..3d3dda95b1 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -62,7 +62,7 @@ bool BaseIndex::Init() if (locator.IsNull()) { m_best_block_index = nullptr; } else { - m_best_block_index = FindForkInGlobalIndex(::ChainActive(), locator); + m_best_block_index = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); } m_synced = m_best_block_index.load() == ::ChainActive().Tip(); return true; @@ -239,7 +239,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator) const CBlockIndex* locator_tip_index; { LOCK(cs_main); - locator_tip_index = LookupBlockIndex(locator_tip_hash); + locator_tip_index = g_chainman.m_blockman.LookupBlockIndex(locator_tip_hash); } if (!locator_tip_index) { diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp index 65a5f03a8e..4f61bbeabd 100644 --- a/src/index/blockfilterindex.cpp +++ b/src/index/blockfilterindex.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -51,7 +51,6 @@ struct DBVal { struct DBHeightKey { int height; - DBHeightKey() : height(0) {} explicit DBHeightKey(int height_in) : height(height_in) {} template<typename Stream> diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h index 317f8c0e40..221ac02c9e 100644 --- a/src/index/blockfilterindex.h +++ b/src/index/blockfilterindex.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,15 +9,11 @@ #include <chain.h> #include <flatfile.h> #include <index/base.h> +#include <util/hasher.h> /** Interval between compact filter checkpoints. See BIP 157. */ static constexpr int CFCHECKPT_INTERVAL = 1000; -struct FilterHeaderHasher -{ - size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } -}; - /** * BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of * blocks by height. An index is constructed for each supported filter type with its own database diff --git a/src/index/disktxpos.h b/src/index/disktxpos.h index 69696b0ec5..3166053226 100644 --- a/src/index/disktxpos.h +++ b/src/index/disktxpos.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 462ac5962f..6398b7edc8 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/init.cpp b/src/init.cpp index 1e41616e94..716c06cd3a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -26,6 +26,7 @@ #include <interfaces/chain.h> #include <interfaces/node.h> #include <key.h> +#include <mapport.h> #include <miner.h> #include <net.h> #include <net_permissions.h> @@ -67,6 +68,8 @@ #include <set> #include <stdint.h> #include <stdio.h> +#include <thread> +#include <vector> #ifndef WIN32 #include <attributes.h> @@ -77,7 +80,6 @@ #include <boost/algorithm/string/replace.hpp> #include <boost/signals2/signal.hpp> -#include <boost/thread/thread.hpp> #if ENABLE_ZMQ #include <zmq/zmqabstractnotifier.h> @@ -85,7 +87,6 @@ #include <zmq/zmqrpc.h> #endif -static bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; @@ -99,8 +100,6 @@ static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; #define MIN_CORE_FILEDESCRIPTORS 150 #endif -static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; - static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map"; /** @@ -113,7 +112,7 @@ static fs::path GetPidFile(const ArgsManager& args) return AbsPathForConfigVal(fs::path(args.GetArg("-pid", BITCOIN_PID_FILENAME))); } -NODISCARD static bool CreatePidFile(const ArgsManager& args) +[[nodiscard]] static bool CreatePidFile(const ArgsManager& args) { fsbridge::ofstream file{GetPidFile(args)}; if (file) { @@ -157,8 +156,6 @@ static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; static std::thread g_load_block; -static boost::thread_group threadGroup; - void Interrupt(NodeContext& node) { InterruptHTTPServer(); @@ -203,7 +200,7 @@ void Shutdown(NodeContext& node) // using the other before destroying them. if (node.peerman) UnregisterValidationInterface(node.peerman.get()); // Follow the lock order requirements: - // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount + // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraFullOutboundCount // which locks cs_vNodes. // * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling ForEachNode which // locks cs_vNodes. @@ -220,11 +217,10 @@ void Shutdown(NodeContext& node) StopTorControl(); // After everything has been shut down, but before things get flushed, stop the - // CScheduler/checkqueue, threadGroup and load block thread. + // CScheduler/checkqueue, scheduler and load block thread. if (node.scheduler) node.scheduler->stop(); if (g_load_block.joinable()) g_load_block.join(); - threadGroup.interrupt_all(); - threadGroup.join_all(); + StopScriptCheckWorkerThreads(); // After the threads that potentially access these pointers have been stopped, // destruct and reset all to nullptr. @@ -236,17 +232,8 @@ void Shutdown(NodeContext& node) DumpMempool(*node.mempool); } - if (fFeeEstimatesInitialized) - { - ::feeEstimator.FlushUnconfirmed(); - fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; - CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION); - if (!est_fileout.IsNull()) - ::feeEstimator.Write(est_fileout); - else - LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string()); - fFeeEstimatesInitialized = false; - } + // Drop transactions we were still watching, and record fee estimations. + if (node.fee_estimator) node.fee_estimator->Flush(); // FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing if (node.chainman) { @@ -304,6 +291,7 @@ void Shutdown(NodeContext& node) globalVerifyHandle.reset(); ECC_Stop(); node.mempool.reset(); + node.fee_estimator.reset(); node.chainman = nullptr; node.scheduler.reset(); @@ -457,11 +445,11 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with ipv4 or ipv6 but not onion and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-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 signet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections.", 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); @@ -479,6 +467,11 @@ void SetupServerArgs(NodeContext& node) #else hidden_args.emplace_back("-upnp"); #endif +#ifdef USE_NATPMP + argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION); +#else + hidden_args.emplace_back("-natpmp"); +#endif // USE_NATPMP argsman.AddArg("-whitebind=<[permissions@]addr>", "Bind to the given address and add permission flags to the peers connecting to it. " "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". " "Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -519,7 +512,6 @@ void SetupServerArgs(NodeContext& node) 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 %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); argsman.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); @@ -710,7 +702,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp if (!file) break; // This error is logged in OpenBlockFile LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); - LoadExternalBlockFile(chainparams, file, &pos); + ::ChainstateActive().LoadExternalBlockFile(chainparams, file, &pos); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; @@ -729,7 +721,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp FILE *file = fsbridge::fopen(path, "rb"); if (file) { LogPrintf("Importing blocks file %s...\n", path.string()); - LoadExternalBlockFile(chainparams, file); + ::ChainstateActive().LoadExternalBlockFile(chainparams, file); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; @@ -824,10 +816,13 @@ void InitParameterInteraction(ArgsManager& args) // to protect privacy, do not listen by default if a default proxy server is specified 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 protect privacy, do not map ports 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 (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -proxy set -> setting -natpmp=0\n", __func__); + } // to protect privacy, do not discover addresses by default if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); @@ -837,6 +832,9 @@ void InitParameterInteraction(ArgsManager& args) // do not map ports or try to retrieve public IP when not listening (pointless) if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -listen=0 -> setting -natpmp=0\n", __func__); + } if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); if (args.SoftSetBoolArg("-listenonion", false)) @@ -914,7 +912,7 @@ std::set<BlockFilterType> g_enabled_filter_types; std::terminate(); }; -bool AppInitBasicSetup(ArgsManager& args) +bool AppInitBasicSetup(const ArgsManager& args) { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -928,6 +926,9 @@ bool AppInitBasicSetup(ArgsManager& args) // Enable heap terminate-on-corruption HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0); #endif + if (!InitShutdownState()) { + return InitError(Untranslated("Initializing wait-for-shutdown state failed.")); + } if (!SetupNetworking()) { return InitError(Untranslated("Initializing networking failed.")); @@ -1331,16 +1332,14 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA LogPrintf("Script verification uses %d additional threads\n", script_threads); if (script_threads >= 1) { g_parallel_script_checks = true; - for (int i = 0; i < script_threads; ++i) { - threadGroup.create_thread([i]() { return ThreadScriptCheck(i); }); - } + StartScriptCheckWorkerThreads(script_threads); } assert(!node.scheduler); node.scheduler = MakeUnique<CScheduler>(); // Start the lightweight task scheduler thread - threadGroup.create_thread([&] { TraceThread("scheduler", [&] { node.scheduler->serviceQueue(); }); }); + node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { node.scheduler->serviceQueue(); }); }); // Gather some entropy once per minute. node.scheduler->scheduleEvery([]{ @@ -1384,27 +1383,31 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA // is not yet setup and may end up being set up twice if we // need to reindex later. + fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN); + fDiscover = args.GetBoolArg("-discover", true); + const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)}; + assert(!node.banman); node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); assert(!node.connman); node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true)); - // 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.fee_estimator); + // Don't initialize fee estimation with old data if we don't relay transactions, + // as they would never get updated. + if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(); + assert(!node.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); - } - } + int check_ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); + node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio); assert(!node.chainman); node.chainman = &g_chainman; ChainstateManager& chainman = *Assert(node.chainman); - node.peerman.reset(new PeerManager(chainparams, *node.connman, node.banman.get(), *node.scheduler, chainman, *node.mempool)); + assert(!node.peerman); + node.peerman = PeerManager::make(chainparams, *node.connman, node.banman.get(), + *node.scheduler, chainman, *node.mempool, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); // sanitize comments per BIP-0014, format user agent and check total size @@ -1480,11 +1483,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA } } - // see Step 2: parameter interactions for more information about these - fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN); - fDiscover = args.GetBoolArg("-discover", true); - g_relay_txes = !args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); - for (const std::string& strAddr : args.GetArgs("-externalip")) { CService addrLocal; if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) @@ -1525,12 +1523,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA RegisterValidationInterface(g_zmq_notification_interface); } #endif - uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set - uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME; - - if (args.IsArgSet("-maxuploadtarget")) { - nMaxOutboundLimit = args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 * 1024; - } // ********************************************************* Step 7: load block chain @@ -1616,7 +1608,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). if (!chainman.BlockIndex().empty() && - !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { + !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); } @@ -1792,13 +1784,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA return false; } - fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; - CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION); - // Allowed to fail as this file IS missing on first startup. - if (!est_filein.IsNull()) - ::feeEstimator.Read(est_filein); - fFeeEstimatesInitialized = true; - // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { g_txindex = MakeUnique<TxIndex>(nTxIndexCache, false, fReindex); @@ -1917,13 +1902,12 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA } } LogPrintf("nBestHeight = %d\n", chain_active_height); + if (node.peerman) node.peerman->SetBestHeight(chain_active_height); Discover(); - // Map ports with UPnP - if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) { - StartMapPort(); - } + // Map ports with UPnP or NAT-PMP. + StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; @@ -1932,7 +1916,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCK_RELAY_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS; - connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; connOptions.m_banman = node.banman.get(); connOptions.m_msgproc = node.peerman.get(); @@ -1940,8 +1923,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); connOptions.m_added_nodes = args.GetArgs("-addnode"); - connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; - connOptions.nMaxOutboundLimit = nMaxOutboundLimit; + connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET); connOptions.m_peer_connect_timeout = peer_connect_timeout; for (const std::string& bind_arg : args.GetArgs("-bind")) { diff --git a/src/init.h b/src/init.h index 679e875da1..c04d966d06 100644 --- a/src/init.h +++ b/src/init.h @@ -33,7 +33,7 @@ void InitParameterInteraction(ArgsManager& args); * @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(ArgsManager& args); +bool AppInitBasicSetup(const ArgsManager& args); /** * Initialization: parameter interaction. * @note This can be done before daemonization. Do not call Shutdown() if this function fails. diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 85d09be0f3..1a49518d69 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -44,6 +44,10 @@ public: FoundBlock& time(int64_t& time) { m_time = &time; return *this; } FoundBlock& maxTime(int64_t& max_time) { m_max_time = &max_time; return *this; } FoundBlock& mtpTime(int64_t& mtp_time) { m_mtp_time = &mtp_time; return *this; } + //! Return whether block is in the active (most-work) chain. + FoundBlock& inActiveChain(bool& in_active_chain) { m_in_active_chain = &in_active_chain; return *this; } + //! Return next block in the active chain if current block is in the active chain. + FoundBlock& nextBlock(const FoundBlock& next_block) { m_next_block = &next_block; return *this; } //! Read block data from disk. If the block exists but doesn't have data //! (for example due to pruning), the CBlock variable will be set to null. FoundBlock& data(CBlock& data) { m_data = &data; return *this; } @@ -53,6 +57,8 @@ public: int64_t* m_time = nullptr; int64_t* m_max_time = nullptr; int64_t* m_mtp_time = nullptr; + bool* m_in_active_chain = nullptr; + const FoundBlock* m_next_block = nullptr; CBlock* m_data = nullptr; }; @@ -77,9 +83,9 @@ public: //! wallet cache it, fee estimation being driven by node mempool, wallet //! should be the consumer. //! -//! * The `guessVerificationProgress`, `getBlockHeight`, `getBlockHash`, etc -//! methods can go away if rescan logic is moved on the node side, and wallet -//! only register rescan request. +//! * `guessVerificationProgress` and similar methods can go away if rescan +//! logic moves out of the wallet, and the wallet just requests scans from the +//! node (https://github.com/bitcoin/bitcoin/issues/11756) class Chain { public: @@ -90,11 +96,6 @@ public: //! any blocks) virtual Optional<int> getHeight() = 0; - //! Get block height above genesis block. Returns 0 for genesis block, - //! 1 for following block, and so on. Returns nullopt for a block not - //! included in the current chain. - virtual Optional<int> getBlockHeight(const uint256& hash) = 0; - //! Get block hash. Height must be valid or this function will abort. virtual uint256 getBlockHash(int height) = 0; @@ -102,13 +103,6 @@ public: //! pruned), and contains transactions. virtual bool haveBlockOnDisk(int height) = 0; - //! Return height of the first block in the chain with timestamp equal - //! or greater than the given time and height equal or greater than the - //! given height, or nullopt if there is no block with a high enough - //! timestamp and height. Also return the block hash as an optional output parameter - //! (to avoid the cost of a second lookup in case this information is needed.) - virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0; - //! Get locator for the current chain tip. virtual CBlockLocator getTipLocator() = 0; @@ -130,11 +124,6 @@ public: //! information. virtual bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block={}) = 0; - //! Find next block if block is part of current chain. Also flag if - //! there was a reorg and the specified block hash is no longer in the - //! current chain, and optionally return block information. - virtual bool findNextBlock(const uint256& block_hash, int block_height, const FoundBlock& next={}, bool* reorg=nullptr) = 0; - //! Find ancestor of block at specified height and optionally return //! ancestor information. virtual bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out={}) = 0; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp deleted file mode 100644 index 2c5f8627e6..0000000000 --- a/src/interfaces/node.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (c) 2018-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include <interfaces/node.h> - -#include <addrdb.h> -#include <banman.h> -#include <chain.h> -#include <chainparams.h> -#include <init.h> -#include <interfaces/chain.h> -#include <interfaces/handler.h> -#include <interfaces/wallet.h> -#include <net.h> -#include <net_processing.h> -#include <netaddress.h> -#include <netbase.h> -#include <node/context.h> -#include <node/ui_interface.h> -#include <policy/feerate.h> -#include <policy/fees.h> -#include <policy/settings.h> -#include <primitives/block.h> -#include <rpc/server.h> -#include <shutdown.h> -#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> -#include <validation.h> -#include <warnings.h> - -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif - -#include <univalue.h> - -#include <boost/signals2/signal.hpp> - -namespace interfaces { -namespace { - -class NodeImpl : public Node -{ -public: - NodeImpl(NodeContext* context) { setContext(context); } - 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(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() && - AppInitLockDataDirectory() && AppInitInterfaces(*m_context); - } - bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override - { - return AppInitMain(m_context_ref, *m_context, tip_info); - } - void appShutdown() override - { - Interrupt(*m_context); - Shutdown(*m_context); - } - void startShutdown() override - { - StartShutdown(); - // Stop RPC for clean shutdown if any of waitfor* commands is executed. - if (gArgs.GetBoolArg("-server", false)) { - InterruptRPC(); - StopRPC(); - } - } - bool shutdownRequested() override { return ShutdownRequested(); } - void mapPort(bool use_upnp) override - { - if (use_upnp) { - StartMapPort(); - } else { - InterruptMapPort(); - StopMapPort(); - } - } - bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } - size_t getNodeCount(CConnman::NumConnections flags) override - { - return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; - } - bool getNodesStats(NodesStats& stats) override - { - stats.clear(); - - if (m_context->connman) { - std::vector<CNodeStats> stats_temp; - m_context->connman->GetNodeStats(stats_temp); - - stats.reserve(stats_temp.size()); - for (auto& node_stats_temp : stats_temp) { - stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); - } - - // Try to retrieve the CNodeStateStats for each node. - TRY_LOCK(::cs_main, lockMain); - if (lockMain) { - for (auto& node_stats : stats) { - std::get<1>(node_stats) = - GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); - } - } - return true; - } - return false; - } - bool getBanned(banmap_t& banmap) override - { - if (m_context->banman) { - m_context->banman->GetBanned(banmap); - return true; - } - return false; - } - bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override - { - if (m_context->banman) { - m_context->banman->Ban(net_addr, ban_time_offset); - return true; - } - return false; - } - bool unban(const CSubNet& ip) override - { - if (m_context->banman) { - m_context->banman->Unban(ip); - return true; - } - return false; - } - bool disconnectByAddress(const CNetAddr& net_addr) override - { - if (m_context->connman) { - return m_context->connman->DisconnectNode(net_addr); - } - return false; - } - bool disconnectById(NodeId id) override - { - if (m_context->connman) { - return m_context->connman->DisconnectNode(id); - } - return false; - } - int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; } - int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } - size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } - size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } - bool getHeaderTip(int& height, int64_t& block_time) override - { - LOCK(::cs_main); - if (::pindexBestHeader) { - height = ::pindexBestHeader->nHeight; - block_time = ::pindexBestHeader->GetBlockTime(); - return true; - } - return false; - } - int getNumBlocks() override - { - LOCK(::cs_main); - return ::ChainActive().Height(); - } - uint256 getBestBlockHash() override - { - const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip()); - return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash(); - } - int64_t getLastBlockTime() override - { - LOCK(::cs_main); - if (::ChainActive().Tip()) { - return ::ChainActive().Tip()->GetBlockTime(); - } - return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network - } - double getVerificationProgress() override - { - const CBlockIndex* tip; - { - LOCK(::cs_main); - tip = ::ChainActive().Tip(); - } - return GuessVerificationProgress(Params().TxData(), tip); - } - bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } - bool getReindex() override { return ::fReindex; } - bool getImporting() override { return ::fImporting; } - void setNetworkActive(bool active) override - { - if (m_context->connman) { - m_context->connman->SetNetworkActive(active); - } - } - bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); } - CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) override - { - FeeCalculation fee_calc; - CFeeRate result = ::feeEstimator.estimateSmartFee(num_blocks, &fee_calc, conservative); - if (returned_target) { - *returned_target = fee_calc.returnedTarget; - } - return result; - } - CFeeRate getDustRelayFee() override { return ::dustRelayFee; } - UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override - { - JSONRPCRequest req(m_context_ref); - req.params = params; - req.strMethod = command; - req.URI = uri; - return ::tableRPC.execute(req); - } - std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); } - void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } - void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } - bool getUnspentOutput(const COutPoint& output, Coin& coin) override - { - LOCK(::cs_main); - return ::ChainstateActive().CoinsTip().GetCoin(output, coin); - } - WalletClient& walletClient() override - { - return *Assert(m_context->wallet_client); - } - std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override - { - return MakeHandler(::uiInterface.InitMessage_connect(fn)); - } - std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override - { - return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); - } - std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override - { - return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); - } - std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override - { - return MakeHandler(::uiInterface.ShowProgress_connect(fn)); - } - std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override - { - return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn)); - } - std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override - { - return MakeHandler(::uiInterface.NotifyNetworkActiveChanged_connect(fn)); - } - std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override - { - return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)); - } - std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override - { - return MakeHandler(::uiInterface.BannedListChanged_connect(fn)); - } - std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override - { - return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) { - fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, - GuessVerificationProgress(Params().TxData(), block)); - })); - } - std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) override - { - return MakeHandler( - ::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) { - fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, - /* verification progress is unused when a header was received */ 0); - })); - } - NodeContext* context() override { return m_context; } - void setContext(NodeContext* context) override - { - m_context = context; - if (context) { - m_context_ref.Set(*context); - } else { - m_context_ref.Clear(); - } - } - NodeContext* m_context{nullptr}; - util::Ref m_context_ref; -}; - -} // namespace - -std::unique_ptr<Node> MakeNode(NodeContext* context) { return MakeUnique<NodeImpl>(context); } - -} // namespace interfaces diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 5079be038e..15f7ef6256 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -82,7 +82,7 @@ public: virtual bool shutdownRequested() = 0; //! Map port. - virtual void mapPort(bool use_upnp) = 0; + virtual void mapPort(bool use_upnp, bool use_natpmp) = 0; //! Get proxy. virtual bool getProxy(Network net, proxyType& proxy_info) = 0; @@ -151,9 +151,6 @@ public: //! Get network active. virtual bool getNetworkActive() = 0; - //! Estimate smart fee. - virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) = 0; - //! Get dust relay fee. virtual CFeeRate getDustRelayFee() = 0; diff --git a/src/key.cpp b/src/key.cpp index 868a8b9b0e..1e59b301cb 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,7 +18,7 @@ static secp256k1_context* secp256k1_context_sign = nullptr; /** * This parses a format loosely based on a DER encoding of the ECPrivateKey type from - * section C.4 of SEC 1 <http://www.secg.org/sec1-v2.pdf>, with the following caveats: + * section C.4 of SEC 1 <https://www.secg.org/sec1-v2.pdf>, with the following caveats: * * * The octet-length of the SEQUENCE must be encoded as 1 or 2 octets. It is not * required to be encoded as one octet if it is less than 256, as DER would require. @@ -80,7 +80,7 @@ int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, con /** * This serializes to a DER encoding of the ECPrivateKey type from section C.4 of SEC 1 - * <http://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are + * <https://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are * included. * * seckey must point to an output buffer of length at least CKey::SIZE bytes. diff --git a/src/key_io.cpp b/src/key_io.cpp index d2f5be93f5..e27673fd16 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -8,16 +8,15 @@ #include <bech32.h> #include <util/strencodings.h> -#include <boost/variant/apply_visitor.hpp> -#include <boost/variant/static_visitor.hpp> - +#include <algorithm> #include <assert.h> #include <string.h> -#include <algorithm> -namespace -{ -class DestinationEncoder : public boost::static_visitor<std::string> +/// Maximum witness length for Bech32 addresses. +static constexpr std::size_t BECH32_WITNESS_PROG_MAX_LEN = 40; + +namespace { +class DestinationEncoder { private: const CChainParams& m_params; @@ -69,10 +68,11 @@ public: std::string operator()(const CNoDestination& no) const { return {}; } }; -CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, std::string& error_str) { std::vector<unsigned char> data; uint160 hash; + error_str = ""; if (DecodeBase58Check(str, data, 21)) { // base58-encoded Bitcoin addresses. // Public-key-hash-addresses have version 0 (or 111 testnet). @@ -89,10 +89,21 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); return ScriptHash(hash); } + + // Set potential error message. + // This message may be changed if the address can also be interpreted as a Bech32 address. + error_str = "Invalid prefix for Base58-encoded address"; } data.clear(); auto bech = bech32::Decode(str); - if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { + if (bech.second.size() > 0) { + error_str = ""; + + if (bech.first != params.Bech32HRP()) { + error_str = "Invalid prefix for Bech32 address"; + return CNoDestination(); + } + // Bech32 decoding int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) // The rest of the symbols are converted witness program bytes. @@ -113,11 +124,21 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par return scriptid; } } + + error_str = "Invalid Bech32 v0 address data size"; return CNoDestination(); } - if (version > 16 || data.size() < 2 || data.size() > 40) { + + if (version > 16) { + error_str = "Invalid Bech32 address witness version"; return CNoDestination(); } + + if (data.size() < 2 || data.size() > BECH32_WITNESS_PROG_MAX_LEN) { + error_str = "Invalid Bech32 address data size"; + return CNoDestination(); + } + WitnessUnknown unk; unk.version = version; std::copy(data.begin(), data.end(), unk.program); @@ -125,6 +146,10 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par return unk; } } + + // Set error message if address can't be interpreted as Base58 or Bech32. + if (error_str.empty()) error_str = "Invalid address format"; + return CNoDestination(); } } // namespace @@ -209,17 +234,24 @@ std::string EncodeExtKey(const CExtKey& key) std::string EncodeDestination(const CTxDestination& dest) { - return boost::apply_visitor(DestinationEncoder(Params()), dest); + return std::visit(DestinationEncoder(Params()), dest); +} + +CTxDestination DecodeDestination(const std::string& str, std::string& error_msg) +{ + return DecodeDestination(str, Params(), error_msg); } CTxDestination DecodeDestination(const std::string& str) { - return DecodeDestination(str, Params()); + std::string error_msg; + return DecodeDestination(str, error_msg); } bool IsValidDestinationString(const std::string& str, const CChainParams& params) { - return IsValidDestination(DecodeDestination(str, params)); + std::string error_msg; + return IsValidDestination(DecodeDestination(str, params, error_msg)); } bool IsValidDestinationString(const std::string& str) diff --git a/src/key_io.h b/src/key_io.h index d80c08f49c..bd81f7847e 100644 --- a/src/key_io.h +++ b/src/key_io.h @@ -23,6 +23,7 @@ std::string EncodeExtPubKey(const CExtPubKey& extpubkey); std::string EncodeDestination(const CTxDestination& dest); CTxDestination DecodeDestination(const std::string& str); +CTxDestination DecodeDestination(const std::string& str, std::string& error_msg); bool IsValidDestinationString(const std::string& str); bool IsValidDestinationString(const std::string& str, const CChainParams& params); diff --git a/src/logging.cpp b/src/logging.cpp index 35e0754f20..4ddcf1d930 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -174,7 +174,7 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str) return false; } -std::vector<LogCategory> BCLog::Logger::LogCategoriesList() +std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const { std::vector<LogCategory> ret; for (const CLogCategoryDesc& category_desc : LogCategories) { diff --git a/src/logging.h b/src/logging.h index 7e646ef67a..9efecc7c12 100644 --- a/src/logging.h +++ b/src/logging.h @@ -135,9 +135,9 @@ namespace BCLog { bool WillLogCategory(LogFlags category) const; /** Returns a vector of the log categories */ - std::vector<LogCategory> LogCategoriesList(); + std::vector<LogCategory> LogCategoriesList() const; /** Returns a string with the log categories */ - std::string LogCategoriesString() + std::string LogCategoriesString() const { return Join(LogCategoriesList(), ", ", [&](const LogCategory& i) { return i.category; }); }; diff --git a/src/mapport.cpp b/src/mapport.cpp new file mode 100644 index 0000000000..2df4ce45d2 --- /dev/null +++ b/src/mapport.cpp @@ -0,0 +1,336 @@ +// Copyright (c) 2011-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#include <mapport.h> + +#include <clientversion.h> +#include <logging.h> +#include <net.h> +#include <netaddress.h> +#include <netbase.h> +#include <threadinterrupt.h> +#include <util/system.h> + +#ifdef USE_NATPMP +#include <compat.h> +#include <natpmp.h> +#endif // USE_NATPMP + +#ifdef USE_UPNP +#include <miniupnpc/miniupnpc.h> +#include <miniupnpc/upnpcommands.h> +#include <miniupnpc/upnperrors.h> +// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility +// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. +static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); +#endif // USE_UPNP + +#include <atomic> +#include <cassert> +#include <chrono> +#include <functional> +#include <string> +#include <thread> + +#if defined(USE_NATPMP) || defined(USE_UPNP) +static CThreadInterrupt g_mapport_interrupt; +static std::thread g_mapport_thread; +static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE}; +static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE}; + +using namespace std::chrono_literals; +static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min}; +static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min}; + +#ifdef USE_NATPMP +static uint16_t g_mapport_external_port = 0; +static bool NatpmpInit(natpmp_t* natpmp) +{ + const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0); + if (r_init == 0) return true; + LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init); + return false; +} + +static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr) +{ + const int r_send = sendpublicaddressrequest(natpmp); + if (r_send == 2 /* OK */) { + int r_read; + natpmpresp_t response; + do { + r_read = readnatpmpresponseorretry(natpmp, &response); + } while (r_read == NATPMP_TRYAGAIN); + + if (r_read == 0) { + external_ipv4_addr = response.pnu.publicaddress.addr; + return true; + } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) { + LogPrintf("natpmp: The gateway does not support NAT-PMP.\n"); + } else { + LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read); + } + } else { + LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send); + } + + return false; +} + +static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered) +{ + const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port; + const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/); + if (r_send == 12 /* OK */) { + int r_read; + natpmpresp_t response; + do { + r_read = readnatpmpresponseorretry(natpmp, &response); + } while (r_read == NATPMP_TRYAGAIN); + + if (r_read == 0) { + auto pm = response.pnu.newportmapping; + if (private_port == pm.privateport && pm.lifetime > 0) { + g_mapport_external_port = pm.mappedpublicport; + const CService external{external_ipv4_addr, pm.mappedpublicport}; + if (!external_ip_discovered && fDiscover) { + AddLocal(external, LOCAL_MAPPED); + external_ip_discovered = true; + } + LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString()); + return true; + } else { + LogPrintf("natpmp: Port mapping failed.\n"); + } + } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) { + LogPrintf("natpmp: The gateway does not support NAT-PMP.\n"); + } else { + LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read); + } + } else { + LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send); + } + + return false; +} + +static bool ProcessNatpmp() +{ + bool ret = false; + natpmp_t natpmp; + struct in_addr external_ipv4_addr; + if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) { + bool external_ip_discovered = false; + const uint16_t private_port = GetListenPort(); + do { + ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered); + } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_mapport_interrupt.reset(); + + const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0); + g_mapport_external_port = 0; + if (r_send == 12 /* OK */) { + LogPrintf("natpmp: Port mapping removed successfully.\n"); + } else { + LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send); + } + } + + closenatpmp(&natpmp); + return ret; +} +#endif // USE_NATPMP + +#ifdef USE_UPNP +static bool ProcessUpnp() +{ + bool ret = false; + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = nullptr; + const char * minissdpdpath = nullptr; + struct UPNPDev * devlist = nullptr; + char lanaddr[64]; + + int error = 0; +#if MINIUPNPC_API_VERSION < 14 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#else + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if (r != UPNPCOMMAND_SUCCESS) { + LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); + } else { + if (externalIPAddress[0]) { + CNetAddr resolved; + if (LookupHost(externalIPAddress, resolved, false)) { + LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); + AddLocal(resolved, LOCAL_MAPPED); + } + } else { + LogPrintf("UPnP: GetExternalIPAddress failed.\n"); + } + } + } + + std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); + + do { + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + + if (r != UPNPCOMMAND_SUCCESS) { + ret = false; + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); + break; + } else { + ret = true; + LogPrintf("UPnP Port Mapping successful.\n"); + } + } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD)); + g_mapport_interrupt.reset(); + + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); + freeUPNPDevlist(devlist); devlist = nullptr; + FreeUPNPUrls(&urls); + } else { + LogPrintf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = nullptr; + if (r != 0) + FreeUPNPUrls(&urls); + } + + return ret; +} +#endif // USE_UPNP + +static void ThreadMapPort() +{ + bool ok; + do { + ok = false; + +#ifdef USE_UPNP + // High priority protocol. + if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) { + g_mapport_current_proto = MapPortProtoFlag::UPNP; + ok = ProcessUpnp(); + if (ok) continue; + } +#endif // USE_UPNP + +#ifdef USE_NATPMP + // Low priority protocol. + if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) { + g_mapport_current_proto = MapPortProtoFlag::NAT_PMP; + ok = ProcessNatpmp(); + if (ok) continue; + } +#endif // USE_NATPMP + + g_mapport_current_proto = MapPortProtoFlag::NONE; + if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + return; + } + + } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD)); +} + +void StartThreadMapPort() +{ + if (!g_mapport_thread.joinable()) { + assert(!g_mapport_interrupt); + g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort)); + } +} + +static void DispatchMapPort() +{ + if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + return; + } + + if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) { + StartThreadMapPort(); + return; + } + + if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) { + InterruptMapPort(); + StopMapPort(); + return; + } + + if (g_mapport_enabled_protos & g_mapport_current_proto) { + // Enabling another protocol does not cause switching from the currently used one. + return; + } + + assert(g_mapport_thread.joinable()); + assert(!g_mapport_interrupt); + // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp() + // to force trying the next protocol in the ThreadMapPort() loop. + g_mapport_interrupt(); +} + +static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) +{ + if (enabled) { + g_mapport_enabled_protos |= proto; + } else { + g_mapport_enabled_protos &= ~proto; + } +} + +void StartMapPort(bool use_upnp, bool use_natpmp) +{ + MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp); + MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp); + DispatchMapPort(); +} + +void InterruptMapPort() +{ + g_mapport_enabled_protos = MapPortProtoFlag::NONE; + if (g_mapport_thread.joinable()) { + g_mapport_interrupt(); + } +} + +void StopMapPort() +{ + if (g_mapport_thread.joinable()) { + g_mapport_thread.join(); + g_mapport_interrupt.reset(); + } +} + +#else // #if defined(USE_NATPMP) || defined(USE_UPNP) +void StartMapPort(bool use_upnp, bool use_natpmp) +{ + // Intentionally left blank. +} +void InterruptMapPort() +{ + // Intentionally left blank. +} +void StopMapPort() +{ + // Intentionally left blank. +} +#endif // #if defined(USE_NATPMP) || defined(USE_UPNP) diff --git a/src/mapport.h b/src/mapport.h new file mode 100644 index 0000000000..279d65167f --- /dev/null +++ b/src/mapport.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-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_MAPPORT_H +#define BITCOIN_MAPPORT_H + +#ifdef USE_UPNP +static constexpr bool DEFAULT_UPNP = USE_UPNP; +#else +static constexpr bool DEFAULT_UPNP = false; +#endif // USE_UPNP + +#ifdef USE_NATPMP +static constexpr bool DEFAULT_NATPMP = USE_NATPMP; +#else +static constexpr bool DEFAULT_NATPMP = false; +#endif // USE_NATPMP + +enum MapPortProtoFlag : unsigned int { + NONE = 0x00, + UPNP = 0x01, + NAT_PMP = 0x02, +}; + +void StartMapPort(bool use_upnp, bool use_natpmp); +void InterruptMapPort(); +void StopMapPort(); + +#endif // BITCOIN_MAPPORT_H diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index b571d463c9..3ffe1465da 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -59,7 +59,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve //if we do not have this assert, we can hit a memory access violation when indexing into vTxid assert(vTxid.size() != 0); if (height == 0) { - // hash at height 0 is the txids themself + // hash at height 0 is the txids themselves return vTxid[pos]; } else { // calculate left hash diff --git a/src/merkleblock.h b/src/merkleblock.h index b2d2828784..0e4ed72130 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/miner.cpp b/src/miner.cpp index 41a835f70a..076d43c951 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -45,7 +45,7 @@ void RegenerateCommitments(CBlock& block) tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block)); block.vtx.at(0) = MakeTransactionRef(tx); - GenerateCoinbaseCommitment(block, WITH_LOCK(cs_main, return LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus()); + GenerateCoinbaseCommitment(block, WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock)), Params().GetConsensus()); block.hashMerkleRoot = BlockMerkleRoot(block); } @@ -176,7 +176,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); BlockValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { + if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); } int64_t nTime2 = GetTimeMicros(); @@ -213,7 +213,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost // - transaction finality (locktime) // - premature witness (in case segwit transactions are added to mempool before // segwit activation) -bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) +bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) const { for (CTxMemPool::txiter it : package) { if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff)) diff --git a/src/miner.h b/src/miner.h index bb7a30b184..9a2b7063f4 100644 --- a/src/miner.h +++ b/src/miner.h @@ -185,7 +185,7 @@ private: * locktime, premature-witness, serialized size (if necessary) * These checks should always succeed, and they're here * only as an extra check in case of suboptimal node configuration */ - bool TestPackageTransactions(const CTxMemPool::setEntries& package); + bool TestPackageTransactions(const CTxMemPool::setEntries& package) const; /** Return true if given transaction from mapTx has already been evaluated, * or if the transaction's cached data in mapTx is incorrect. */ bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set& mapModifiedTx, CTxMemPool::setEntries& failedTx) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs); diff --git a/src/net.cpp b/src/net.cpp index 9c6d7b6375..76bf7effa4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,6 +16,7 @@ #include <net_permissions.h> #include <netbase.h> #include <node/ui_interface.h> +#include <optional.h> #include <protocol.h> #include <random.h> #include <scheduler.h> @@ -32,17 +33,9 @@ #include <poll.h> #endif -#ifdef USE_UPNP -#include <miniupnpc/miniupnpc.h> -#include <miniupnpc/upnpcommands.h> -#include <miniupnpc/upnperrors.h> -// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility -// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. -static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); -#endif - #include <algorithm> #include <cstdint> +#include <functional> #include <unordered_map> #include <math.h> @@ -72,6 +65,9 @@ static constexpr std::chrono::seconds DNSSEEDS_DELAY_FEW_PEERS{11}; static constexpr std::chrono::minutes DNSSEEDS_DELAY_MANY_PEERS{5}; static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000; // "many" vs "few" peers +/** The default timeframe for -maxuploadtarget. 1 day. */ +static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24}; + // We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. #define FEELER_SLEEP_WINDOW 1 @@ -111,7 +107,6 @@ static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256 // bool fDiscover = true; bool fListen = true; -bool g_relay_txes = !DEFAULT_BLOCKSONLY; RecursiveMutex cs_mapLocalHost; std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost); static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {}; @@ -478,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 : "", conn_type); + CNode* pnode = new CNode(id, nLocalServices, 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) @@ -504,9 +499,9 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNet } } -std::string CNode::ConnectionTypeAsString() const +std::string ConnectionTypeAsString(ConnectionType conn_type) { - switch (m_conn_type) { + switch (conn_type) { case ConnectionType::INBOUND: return "inbound"; case ConnectionType::MANUAL: @@ -552,7 +547,7 @@ void CNode::SetAddrLocal(const CService& addrLocalIn) { Network CNode::ConnectedThroughNetwork() const { - return IsInboundConn() && m_inbound_onion ? NET_ONION : addr.GetNetClass(); + return m_inbound_onion ? NET_ONION : addr.GetNetClass(); } #undef X @@ -563,7 +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_network = ConnectedThroughNetwork(); stats.m_mapped_as = addr.GetMappedAS(m_asmap); if (m_tx_relay != nullptr) { LOCK(m_tx_relay->cs_filter); @@ -584,8 +579,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) X(cleanSubVer); } stats.fInbound = IsInboundConn(); - stats.m_manual_connection = IsManualConn(); - X(nStartingHeight); + X(m_bip152_highbandwidth_to); + X(m_bip152_highbandwidth_from); { LOCK(cs_vSend); X(mapSendBytesPerMsgCmd); @@ -596,10 +591,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) X(mapRecvBytesPerMsgCmd); X(nRecvBytes); } - X(m_legacyWhitelisted); X(m_permissionFlags); if (m_tx_relay != nullptr) { - LOCK(m_tx_relay->cs_feeFilter); stats.minFeeFilter = m_tx_relay->minFeeFilter; } else { stats.minFeeFilter = 0; @@ -625,7 +618,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) CService addrLocalUnlocked = GetAddrLocal(); stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; - stats.m_conn_type_string = ConnectionTypeAsString(); + X(m_conn_type); } #undef X @@ -788,30 +781,30 @@ void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vec CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0, hdr}; } -size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend) +size_t CConnman::SocketSendData(CNode& node) const { - auto it = pnode->vSendMsg.begin(); + auto it = node.vSendMsg.begin(); size_t nSentSize = 0; - while (it != pnode->vSendMsg.end()) { - const auto &data = *it; - assert(data.size() > pnode->nSendOffset); + while (it != node.vSendMsg.end()) { + const auto& data = *it; + assert(data.size() > node.nSendOffset); int nBytes = 0; { - LOCK(pnode->cs_hSocket); - if (pnode->hSocket == INVALID_SOCKET) + LOCK(node.cs_hSocket); + if (node.hSocket == INVALID_SOCKET) break; - nBytes = send(pnode->hSocket, reinterpret_cast<const char*>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + nBytes = send(node.hSocket, reinterpret_cast<const char*>(data.data()) + node.nSendOffset, data.size() - node.nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); } if (nBytes > 0) { - pnode->nLastSend = GetSystemTimeInSeconds(); - pnode->nSendBytes += nBytes; - pnode->nSendOffset += nBytes; + node.nLastSend = GetSystemTimeInSeconds(); + node.nSendBytes += nBytes; + node.nSendOffset += nBytes; nSentSize += nBytes; - if (pnode->nSendOffset == data.size()) { - pnode->nSendOffset = 0; - pnode->nSendSize -= data.size(); - pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize; + if (node.nSendOffset == data.size()) { + node.nSendOffset = 0; + node.nSendSize -= data.size(); + node.fPauseSend = node.nSendSize > nSendBufferMaxSize; it++; } else { // could not send full message; stop sending more @@ -821,10 +814,9 @@ size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pno if (nBytes < 0) { // error int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - LogPrintf("socket send error %s\n", NetworkErrorString(nErr)); - pnode->CloseSocketDisconnect(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { + LogPrint(BCLog::NET, "socket send error for peer=%d: %s\n", node.GetId(), NetworkErrorString(nErr)); + node.CloseSocketDisconnect(); } } // couldn't send anything at all @@ -832,29 +824,14 @@ size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pno } } - if (it == pnode->vSendMsg.end()) { - assert(pnode->nSendOffset == 0); - assert(pnode->nSendSize == 0); + if (it == node.vSendMsg.end()) { + assert(node.nSendOffset == 0); + assert(node.nSendSize == 0); } - pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); + node.vSendMsg.erase(node.vSendMsg.begin(), it); return nSentSize; } -struct NodeEvictionCandidate -{ - NodeId id; - int64_t nTimeConnected; - int64_t nMinPingUsecTime; - int64_t nLastBlockTime; - int64_t nLastTXTime; - bool fRelevantServices; - bool fRelayTxes; - bool fBloomFilter; - uint64_t nKeyedNetGroup; - bool prefer_evict; - bool m_is_local; -}; - static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nMinPingUsecTime > b.nMinPingUsecTime; @@ -910,43 +887,8 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, elements.erase(elements.end() - eraseSize, elements.end()); } -/** Try to find a connection to evict when the node is full. - * Extreme care must be taken to avoid opening the node to attacker - * triggered network partitioning. - * The strategy used here is to protect a small number of peers - * for each of several distinct characteristics which are difficult - * to forge. In order to partition a node the attacker must be - * simultaneously better at all of them than honest peers. - */ -bool CConnman::AttemptToEvictConnection() +[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates) { - std::vector<NodeEvictionCandidate> vEvictionCandidates; - { - LOCK(cs_vNodes); - - for (const CNode* node : vNodes) { - if (node->HasPermission(PF_NOBAN)) - continue; - if (!node->IsInboundConn()) - continue; - if (node->fDisconnect) - continue; - bool peer_relay_txes = false; - bool peer_filter_not_null = false; - if (node->m_tx_relay != nullptr) { - LOCK(node->m_tx_relay->cs_filter); - peer_relay_txes = node->m_tx_relay->fRelayTxes; - peer_filter_not_null = node->m_tx_relay->pfilter != nullptr; - } - NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, - node->nLastBlockTime, node->nLastTXTime, - HasAllDesirableServiceFlags(node->nServices), - peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup, - node->m_prefer_evict, node->addr.IsLocal()}; - vEvictionCandidates.push_back(candidate); - } - } - // Protect connections with certain characteristics // Deterministically select 4 peers to protect by netgroup. @@ -984,7 +926,7 @@ bool CConnman::AttemptToEvictConnection() total_protect_size -= initial_size - vEvictionCandidates.size(); EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size); - if (vEvictionCandidates.empty()) return false; + if (vEvictionCandidates.empty()) return nullopt; // If any remaining peers are preferred for eviction consider only them. // This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks) @@ -1016,10 +958,53 @@ bool CConnman::AttemptToEvictConnection() vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); // Disconnect from the network group with the most connections - NodeId evicted = vEvictionCandidates.front().id; + return vEvictionCandidates.front().id; +} + +/** Try to find a connection to evict when the node is full. + * Extreme care must be taken to avoid opening the node to attacker + * triggered network partitioning. + * The strategy used here is to protect a small number of peers + * for each of several distinct characteristics which are difficult + * to forge. In order to partition a node the attacker must be + * simultaneously better at all of them than honest peers. + */ +bool CConnman::AttemptToEvictConnection() +{ + std::vector<NodeEvictionCandidate> vEvictionCandidates; + { + + LOCK(cs_vNodes); + for (const CNode* node : vNodes) { + if (node->HasPermission(PF_NOBAN)) + continue; + if (!node->IsInboundConn()) + continue; + if (node->fDisconnect) + continue; + bool peer_relay_txes = false; + bool peer_filter_not_null = false; + if (node->m_tx_relay != nullptr) { + LOCK(node->m_tx_relay->cs_filter); + peer_relay_txes = node->m_tx_relay->fRelayTxes; + peer_filter_not_null = node->m_tx_relay->pfilter != nullptr; + } + NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, + node->nLastBlockTime, node->nLastTXTime, + HasAllDesirableServiceFlags(node->nServices), + peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup, + node->m_prefer_evict, node->addr.IsLocal()}; + vEvictionCandidates.push_back(candidate); + } + } + const Optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates)); + if (!node_id_to_evict) { + return false; + } LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if (pnode->GetId() == evicted) { + if (pnode->GetId() == *node_id_to_evict) { + LogPrint(BCLog::NET, "selected %s connection for eviction peer=%d; disconnecting\n", pnode->ConnectionTypeAsString(), pnode->GetId()); pnode->fDisconnect = true; return true; } @@ -1044,14 +1029,12 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE; hListenSocket.AddSocketPermissionFlags(permissionFlags); AddWhitelistPermissionFlags(permissionFlags, addr); - bool legacyWhitelisted = false; if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) { NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT); if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY); if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, PF_RELAY); NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL); NetPermissions::AddFlag(permissionFlags, PF_NOBAN); - legacyWhitelisted = true; } { @@ -1070,7 +1053,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { } if (!fNetworkActive) { - LogPrintf("connection from %s dropped: not accepting new connections\n", addr.ToString()); + LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToString()); CloseSocket(hSocket); return; } @@ -1124,11 +1107,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { } 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); + CNode* pnode = new CNode(id, nodeServices, 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) - pnode->m_legacyWhitelisted = legacyWhitelisted; pnode->m_prefer_evict = discouraged; m_msgproc->InitializeNode(pnode); @@ -1143,6 +1124,27 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { RandAddEvent((uint32_t)id); } +bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type) +{ + if (conn_type != ConnectionType::OUTBOUND_FULL_RELAY && conn_type != ConnectionType::BLOCK_RELAY) return false; + + const int max_connections = conn_type == ConnectionType::OUTBOUND_FULL_RELAY ? m_max_outbound_full_relay : m_max_outbound_block_relay; + + // Count existing connections + int existing_connections = WITH_LOCK(cs_vNodes, + return std::count_if(vNodes.begin(), vNodes.end(), [conn_type](CNode* node) { return node->m_conn_type == conn_type; });); + + // Max connections of specified type already exist + if (existing_connections >= max_connections) return false; + + // Max total outbound connections already exist + CSemaphoreGrant grant(*semOutbound, true); + if (!grant) return false; + + OpenNetworkConnection(CAddress(), false, &grant, address.c_str(), conn_type); + return true; +} + void CConnman::DisconnectNodes() { { @@ -1216,37 +1218,47 @@ void CConnman::NotifyNumConnectionsChanged() } } -void CConnman::InactivityCheck(CNode *pnode) +bool CConnman::InactivityCheck(const CNode& node) const { - int64_t nTime = GetSystemTimeInSeconds(); - if (nTime - pnode->nTimeConnected > m_peer_connect_timeout) - { - if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) - { - LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d from %d\n", m_peer_connect_timeout, pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId()); - pnode->fDisconnect = true; - } - else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) - { - LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); - pnode->fDisconnect = true; - } - 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; - } - else if (pnode->nPingNonceSent && pnode->m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL} < GetTime<std::chrono::microseconds>()) - { - LogPrintf("ping timeout: %fs\n", 0.000001 * count_microseconds(GetTime<std::chrono::microseconds>() - pnode->m_ping_start.load())); - pnode->fDisconnect = true; - } - else if (!pnode->fSuccessfullyConnected) - { - LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId()); - pnode->fDisconnect = true; - } + // Use non-mockable system time (otherwise these timers will pop when we + // use setmocktime in the tests). + int64_t now = GetSystemTimeInSeconds(); + + if (now <= node.nTimeConnected + m_peer_connect_timeout) { + // Only run inactivity checks if the peer has been connected longer + // than m_peer_connect_timeout. + return false; + } + + if (node.nLastRecv == 0 || node.nLastSend == 0) { + LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d peer=%d\n", m_peer_connect_timeout, node.nLastRecv != 0, node.nLastSend != 0, node.GetId()); + return true; + } + + if (now > node.nLastSend + TIMEOUT_INTERVAL) { + LogPrint(BCLog::NET, "socket sending timeout: %is peer=%d\n", now - node.nLastSend, node.GetId()); + return true; + } + + if (now > node.nLastRecv + TIMEOUT_INTERVAL) { + LogPrint(BCLog::NET, "socket receive timeout: %is peer=%d\n", now - node.nLastRecv, node.GetId()); + return true; + } + + if (node.nPingNonceSent && node.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL} < GetTime<std::chrono::microseconds>()) { + // We use mockable time for ping timeouts. This means that setmocktime + // may cause pings to time out for peers that have been connected for + // longer than m_peer_connect_timeout. + LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(GetTime<std::chrono::microseconds>() - node.m_ping_start.load()), node.GetId()); + return true; + } + + if (!node.fSuccessfullyConnected) { + LogPrint(BCLog::NET, "version handshake timeout peer=%d\n", node.GetId()); + return true; } + + return false; } bool CConnman::GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set) @@ -1516,19 +1528,13 @@ void CConnman::SocketHandler() } } - // - // Send - // - if (sendSet) - { - LOCK(pnode->cs_vSend); - size_t nBytes = SocketSendData(pnode); - if (nBytes) { - RecordBytesSent(nBytes); - } + if (sendSet) { + // Send data + size_t bytes_sent = WITH_LOCK(pnode->cs_vSend, return SocketSendData(*pnode)); + if (bytes_sent) RecordBytesSent(bytes_sent); } - InactivityCheck(pnode); + if (InactivityCheck(*pnode)) pnode->fDisconnect = true; } { LOCK(cs_vNodes); @@ -1556,121 +1562,6 @@ void CConnman::WakeMessageHandler() condMsgProc.notify_one(); } - - - - - -#ifdef USE_UPNP -static CThreadInterrupt g_upnp_interrupt; -static std::thread g_upnp_thread; -static void ThreadMapPort() -{ - std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = nullptr; - const char * minissdpdpath = nullptr; - struct UPNPDev * devlist = nullptr; - char lanaddr[64]; - - int error = 0; -#if MINIUPNPC_API_VERSION < 14 - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); -#else - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); -#endif - - struct UPNPUrls urls; - struct IGDdatas data; - int r; - - r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - if (r == 1) - { - if (fDiscover) { - char externalIPAddress[40]; - r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); - } else { - if (externalIPAddress[0]) { - CNetAddr resolved; - if (LookupHost(externalIPAddress, resolved, false)) { - LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString()); - AddLocal(resolved, LOCAL_UPNP); - } - } else { - LogPrintf("UPnP: GetExternalIPAddress failed.\n"); - } - } - } - - std::string strDesc = PACKAGE_NAME " " + FormatFullVersion(); - - do { - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); - - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); - } else { - LogPrintf("UPnP Port Mapping successful.\n"); - } - } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); - - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = nullptr; - FreeUPNPUrls(&urls); - } else { - LogPrintf("No valid UPnP IGDs found\n"); - freeUPNPDevlist(devlist); devlist = nullptr; - if (r != 0) - FreeUPNPUrls(&urls); - } -} - -void StartMapPort() -{ - if (!g_upnp_thread.joinable()) { - assert(!g_upnp_interrupt); - g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); - } -} - -void InterruptMapPort() -{ - if(g_upnp_thread.joinable()) { - g_upnp_interrupt(); - } -} - -void StopMapPort() -{ - if(g_upnp_thread.joinable()) { - g_upnp_thread.join(); - g_upnp_interrupt.reset(); - } -} - -#else -void StartMapPort() -{ - // Intentionally left blank. -} -void InterruptMapPort() -{ - // Intentionally left blank. -} -void StopMapPort() -{ - // Intentionally left blank. -} -#endif - - - - - - void CConnman::ThreadDNSAddressSeed() { FastRandomContext rng; @@ -1826,18 +1717,32 @@ void CConnman::SetTryNewOutboundPeer(bool flag) // Also exclude peers that haven't finished initial connection handshake yet // (so that we don't decide we're over our desired connection limit, and then // evict some peer that has finished the handshake) -int CConnman::GetExtraOutboundCount() +int CConnman::GetExtraFullOutboundCount() +{ + int full_outbound_peers = 0; + { + LOCK(cs_vNodes); + for (const CNode* pnode : vNodes) { + if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsFullOutboundConn()) { + ++full_outbound_peers; + } + } + } + return std::max(full_outbound_peers - m_max_outbound_full_relay, 0); +} + +int CConnman::GetExtraBlockRelayCount() { - int nOutbound = 0; + int block_relay_peers = 0; { LOCK(cs_vNodes); for (const CNode* pnode : vNodes) { - if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsOutboundOrBlockRelayConn()) { - ++nOutbound; + if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsBlockOnlyConn()) { + ++block_relay_peers; } } } - return std::max(nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0); + return std::max(block_relay_peers - m_max_outbound_block_relay, 0); } void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) @@ -1868,6 +1773,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // Minimum time before next feeler connection (in microseconds). int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL); + int64_t nNextExtraBlockRelay = PoissonNextSend(nStart*1000*1000, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL); while (!interruptNet) { ProcessAddrFetch(); @@ -1940,8 +1846,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // 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. + // these conditions are met, check to see if it's time to try an extra + // block-relay-only peer (to confirm our tip is current, see below) or the nNextFeeler + // timer to decide if we should open a FEELER. if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) { conn_type = ConnectionType::BLOCK_RELAY; @@ -1952,6 +1859,30 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) conn_type = ConnectionType::BLOCK_RELAY; } else if (GetTryNewOutboundPeer()) { // OUTBOUND_FULL_RELAY + } else if (nTime > nNextExtraBlockRelay && m_start_extra_block_relay_peers) { + // Periodically connect to a peer (using regular outbound selection + // methodology from addrman) and stay connected long enough to sync + // headers, but not much else. + // + // Then disconnect the peer, if we haven't learned anything new. + // + // The idea is to make eclipse attacks very difficult to pull off, + // because every few minutes we're finding a new peer to learn headers + // from. + // + // This is similar to the logic for trying extra outbound (full-relay) + // peers, except: + // - we do this all the time on a poisson timer, rather than just when + // our tip is stale + // - we potentially disconnect our next-youngest block-relay-only peer, if our + // newest block-relay-only peer delivers a block more recently. + // See the eviction logic in net_processing.cpp. + // + // Because we can promote these connections to block-relay-only + // connections, they do not get their own ConnectionType enum + // (similar to how we deal with extra outbound peers). + nNextExtraBlockRelay = PoissonNextSend(nTime, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL); + conn_type = ConnectionType::BLOCK_RELAY; } else if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); conn_type = ConnectionType::FEELER; @@ -2037,7 +1968,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) continue; } - // do not allow non-default ports, unless after 50 invalid addresses selected already + // Do not allow non-default ports, unless after 50 invalid + // addresses selected already. This is to prevent malicious peers + // from advertising themselves as a service on another host and + // port, causing a DoS attack as nodes around the network attempt + // to connect to it fruitlessly. if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50) continue; @@ -2759,6 +2694,7 @@ bool CConnman::DisconnectNode(const std::string& strNode) { LOCK(cs_vNodes); if (CNode* pnode = FindNode(strNode)) { + LogPrint(BCLog::NET, "disconnect by address%s matched peer=%d; disconnecting\n", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->GetId()); pnode->fDisconnect = true; return true; } @@ -2771,6 +2707,7 @@ bool CConnman::DisconnectNode(const CSubNet& subnet) LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (subnet.Match(pnode->addr)) { + LogPrint(BCLog::NET, "disconnect by subnet%s matched peer=%d; disconnecting\n", (fLogIPs ? strprintf("=%s", subnet.ToString()) : ""), pnode->GetId()); pnode->fDisconnect = true; disconnected = true; } @@ -2788,6 +2725,7 @@ bool CConnman::DisconnectNode(NodeId id) LOCK(cs_vNodes); for(CNode* pnode : vNodes) { if (id == pnode->GetId()) { + LogPrint(BCLog::NET, "disconnect by id peer=%d; disconnecting\n", pnode->GetId()); pnode->fDisconnect = true; return true; } @@ -2806,8 +2744,8 @@ void CConnman::RecordBytesSent(uint64_t bytes) LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; - uint64_t now = GetTime(); - if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) + const auto now = GetTime<std::chrono::seconds>(); + if (nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME < now) { // timeframe expired, reset cycle nMaxOutboundCycleStartTime = now; @@ -2818,48 +2756,29 @@ void CConnman::RecordBytesSent(uint64_t bytes) nMaxOutboundTotalBytesSentInCycle += bytes; } -void CConnman::SetMaxOutboundTarget(uint64_t limit) -{ - LOCK(cs_totalBytesSent); - nMaxOutboundLimit = limit; -} - uint64_t CConnman::GetMaxOutboundTarget() { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } -uint64_t CConnman::GetMaxOutboundTimeframe() +std::chrono::seconds CConnman::GetMaxOutboundTimeframe() { - LOCK(cs_totalBytesSent); - return nMaxOutboundTimeframe; + return MAX_UPLOAD_TIMEFRAME; } -uint64_t CConnman::GetMaxOutboundTimeLeftInCycle() +std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) - return 0; + return 0s; - if (nMaxOutboundCycleStartTime == 0) - return nMaxOutboundTimeframe; + if (nMaxOutboundCycleStartTime.count() == 0) + return MAX_UPLOAD_TIMEFRAME; - uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe; - uint64_t now = GetTime(); - return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); -} - -void CConnman::SetMaxOutboundTimeframe(uint64_t timeframe) -{ - LOCK(cs_totalBytesSent); - if (nMaxOutboundTimeframe != timeframe) - { - // reset measure-cycle in case of changing - // the timeframe - nMaxOutboundCycleStartTime = GetTime(); - } - nMaxOutboundTimeframe = timeframe; + const std::chrono::seconds cycleEndTime = nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME; + const auto now = GetTime<std::chrono::seconds>(); + return (cycleEndTime < now) ? 0s : cycleEndTime - now; } bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) @@ -2871,8 +2790,8 @@ bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) if (historicalBlockServingLimit) { // keep a large enough buffer to at least relay each block once - uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); - uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SERIALIZED_SIZE; + const std::chrono::seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); + const uint64_t buffer = timeLeftInCycle / std::chrono::minutes{10} * MAX_BLOCK_SERIALIZED_SIZE; if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) return true; } @@ -2908,36 +2827,22 @@ ServiceFlags CConnman::GetLocalServices() const return nLocalServices; } -void CConnman::SetBestHeight(int height) -{ - nBestHeight.store(height, std::memory_order_release); -} - -int CConnman::GetBestHeight() const -{ - return nBestHeight.load(std::memory_order_acquire); -} - 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, ConnectionType conn_type_in, bool inbound_onion) +CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion) : nTimeConnected(GetSystemTimeInSeconds()), - addr(addrIn), - addrBind(addrBindIn), - 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). - id(idIn), - nLocalHostNonce(nLocalHostNonceIn), - m_conn_type(conn_type_in), - nLocalServices(nLocalServicesIn), - nMyStartingHeight(nMyStartingHeightIn), - m_inbound_onion(inbound_onion) -{ + addr(addrIn), + addrBind(addrBindIn), + nKeyedNetGroup(nKeyedNetGroupIn), + id(idIn), + nLocalHostNonce(nLocalHostNonceIn), + m_conn_type(conn_type_in), + nLocalServices(nLocalServicesIn), + m_inbound_onion(inbound_onion) +{ + if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND); hSocket = hSocketIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; - hashContinue = uint256(); if (conn_type_in != ConnectionType::BLOCK_RELAY) { m_tx_relay = MakeUnique<TxRelay>(); } @@ -2997,7 +2902,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) // If write queue empty, attempt "optimistic write" if (optimisticSend == true) - nBytesSent = SocketSendData(pnode); + nBytesSent = SocketSendData(*pnode); } if (nBytesSent) RecordBytesSent(nBytesSent); @@ -24,19 +24,16 @@ #include <sync.h> #include <threadinterrupt.h> #include <uint256.h> +#include <util/check.h> #include <atomic> +#include <condition_variable> #include <cstdint> #include <deque> #include <map> -#include <thread> #include <memory> -#include <condition_variable> - -#ifndef WIN32 -#include <arpa/inet.h> -#endif - +#include <thread> +#include <vector> class CScheduler; class CNode; @@ -52,6 +49,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; +/** Run the extra block-relay-only connection loop once every 5 minutes. **/ +static const int EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 300; /** 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). */ @@ -68,18 +67,10 @@ static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2; static const int MAX_FEELER_CONNECTIONS = 1; /** -listen default */ static const bool DEFAULT_LISTEN = true; -/** -upnp default */ -#ifdef USE_UPNP -static const bool DEFAULT_UPNP = USE_UPNP; -#else -static const bool DEFAULT_UPNP = false; -#endif /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ -static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; -/** The default timeframe for -maxuploadtarget. 1 day. */ -static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24; +static constexpr uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; /** Default for blocks only*/ static const bool DEFAULT_BLOCKSONLY = false; /** -peertimeout default */ @@ -120,7 +111,8 @@ struct CSerializedNetMsg * 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. */ + * src/rpc/net.cpp and src/qt/rpcconsole.cpp, as well as the descriptions in + * src/qt/guiutil.cpp and src/bitcoin-cli.cpp::NetinfoRequestHandler. */ enum class ConnectionType { /** * Inbound connections are those initiated by a peer. This is the only @@ -131,7 +123,7 @@ enum class ConnectionType { /** * 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 + * 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. */ @@ -139,8 +131,8 @@ enum class ConnectionType { /** - * We open manual connections to addresses that users explicitly inputted - * via the addnode RPC, or the -connect command line argument. Even if a + * We open manual connections to addresses that users explicitly requested + * via the addnode RPC or the -addnode/-connect configuration options. Even if a * manual connection is misbehaving, we do not automatically disconnect or * add it to our discouragement filter. */ @@ -159,7 +151,7 @@ enum class ConnectionType { * 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. + * otherwise we connect to a node from the new table. */ FEELER, @@ -183,463 +175,17 @@ enum class ConnectionType { ADDR_FETCH, }; -class NetEventsInterface; -class CConnman -{ -public: - - enum NumConnections { - CONNECTIONS_NONE = 0, - CONNECTIONS_IN = (1U << 0), - CONNECTIONS_OUT = (1U << 1), - CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), - }; - - struct Options - { - ServiceFlags nLocalServices = NODE_NONE; - int nMaxConnections = 0; - int m_max_outbound_full_relay = 0; - int m_max_outbound_block_relay = 0; - int nMaxAddnode = 0; - int nMaxFeeler = 0; - int nBestHeight = 0; - CClientUIInterface* uiInterface = nullptr; - NetEventsInterface* m_msgproc = nullptr; - BanMan* m_banman = nullptr; - unsigned int nSendBufferMaxSize = 0; - unsigned int nReceiveFloodSize = 0; - uint64_t nMaxOutboundTimeframe = 0; - uint64_t nMaxOutboundLimit = 0; - int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT; - std::vector<std::string> vSeedNodes; - 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; - std::vector<bool> m_asmap; - }; - - void Init(const Options& connOptions) { - nLocalServices = connOptions.nLocalServices; - nMaxConnections = connOptions.nMaxConnections; - m_max_outbound_full_relay = std::min(connOptions.m_max_outbound_full_relay, connOptions.nMaxConnections); - m_max_outbound_block_relay = connOptions.m_max_outbound_block_relay; - m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing; - nMaxAddnode = connOptions.nMaxAddnode; - nMaxFeeler = connOptions.nMaxFeeler; - m_max_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + nMaxFeeler; - nBestHeight = connOptions.nBestHeight; - clientInterface = connOptions.uiInterface; - m_banman = connOptions.m_banman; - m_msgproc = connOptions.m_msgproc; - nSendBufferMaxSize = connOptions.nSendBufferMaxSize; - nReceiveFloodSize = connOptions.nReceiveFloodSize; - m_peer_connect_timeout = connOptions.m_peer_connect_timeout; - { - LOCK(cs_totalBytesSent); - nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; - nMaxOutboundLimit = connOptions.nMaxOutboundLimit; - } - vWhitelistedRange = connOptions.vWhitelistedRange; - { - 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); - ~CConnman(); - bool Start(CScheduler& scheduler, const Options& options); - - void StopThreads(); - void StopNodes(); - void Stop() - { - StopThreads(); - StopNodes(); - }; - - void Interrupt(); - 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, 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); - - using NodeFn = std::function<void(CNode*)>; - void ForEachNode(const NodeFn& func) - { - LOCK(cs_vNodes); - for (auto&& node : vNodes) { - if (NodeFullyConnected(node)) - func(node); - } - }; - - void ForEachNode(const NodeFn& func) const - { - LOCK(cs_vNodes); - for (auto&& node : vNodes) { - if (NodeFullyConnected(node)) - func(node); - } - }; - - template<typename Callable, typename CallableAfter> - void ForEachNodeThen(Callable&& pre, CallableAfter&& post) - { - LOCK(cs_vNodes); - for (auto&& node : vNodes) { - if (NodeFullyConnected(node)) - pre(node); - } - post(); - }; - - template<typename Callable, typename CallableAfter> - void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const - { - LOCK(cs_vNodes); - for (auto&& node : vNodes) { - if (NodeFullyConnected(node)) - pre(node); - } - post(); - }; - - // Addrman functions - void SetServices(const CService &addr, ServiceFlags nServices); - void MarkAddressGood(const CAddress& addr); - 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(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. - void SetTryNewOutboundPeer(bool flag); - bool GetTryNewOutboundPeer(); - - // Return the number of outbound peers we have in excess of our target (eg, - // if we previously called SetTryNewOutboundPeer(true), and have since set - // to false, we may have extra peers that we wish to disconnect). This may - // return a value less than (num_outbound_connections - num_outbound_slots) - // in cases where some outbound connections are not yet fully connected, or - // not yet fully disconnected. - int GetExtraOutboundCount(); - - bool AddNode(const std::string& node); - bool RemoveAddedNode(const std::string& node); - std::vector<AddedNodeInfo> GetAddedNodeInfo(); - - size_t GetNodeCount(NumConnections num); - void GetNodeStats(std::vector<CNodeStats>& vstats); - bool DisconnectNode(const std::string& node); - bool DisconnectNode(const CSubNet& subnet); - bool DisconnectNode(const CNetAddr& addr); - bool DisconnectNode(NodeId id); - - //! Used to convey which local services we are offering peers during node - //! connection. - //! - //! The data returned by this is used in CNode construction, - //! which is used to advertise which services we are offering - //! that peer during `net_processing.cpp:PushNodeVersion()`. - ServiceFlags GetLocalServices() const; - - //!set the max outbound target in bytes - void SetMaxOutboundTarget(uint64_t limit); - uint64_t GetMaxOutboundTarget(); - - //!set the timeframe for the max outbound target - void SetMaxOutboundTimeframe(uint64_t timeframe); - uint64_t GetMaxOutboundTimeframe(); - - //! check if the outbound target is reached - //! if param historicalBlockServingLimit is set true, the function will - //! response true if the limit for serving historical blocks has been reached - bool OutboundTargetReached(bool historicalBlockServingLimit); - - //! response the bytes left in the current max outbound cycle - //! in case of no limit, it will always response 0 - uint64_t GetOutboundTargetBytesLeft(); - - //! response the time in second left in the current max outbound cycle - //! in case of no limit, it will always response 0 - uint64_t GetMaxOutboundTimeLeftInCycle(); - - uint64_t GetTotalBytesRecv(); - uint64_t GetTotalBytesSent(); - - void SetBestHeight(int height); - int GetBestHeight() const; - - /** Get a unique deterministic randomizer. */ - CSipHasher GetDeterministicRandomizer(uint64_t id) const; - - unsigned int GetReceiveFloodSize() const; - - void WakeMessageHandler(); - - /** Attempts to obfuscate tx time through exponentially distributed emitting. - Works assuming that a single interval is used. - Variable intervals will result in privacy decrease. - */ - int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds); - - void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); } - -private: - struct ListenSocket { - public: - SOCKET socket; - inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); } - ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {} - private: - NetPermissionFlags m_permissions; - }; - - bool BindListenPort(const CService& bindAddr, 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, - const std::vector<CService>& onion_binds); - - void ThreadOpenAddedConnections(); - void AddAddrFetch(const std::string& strDest); - void ProcessAddrFetch(); - void ThreadOpenConnections(std::vector<std::string> connect); - void ThreadMessageHandler(); - void AcceptConnection(const ListenSocket& hListenSocket); - void DisconnectNodes(); - void NotifyNumConnectionsChanged(); - void InactivityCheck(CNode *pnode); - bool GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set); - void SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set); - void SocketHandler(); - void ThreadSocketHandler(); - void ThreadDNSAddressSeed(); - - uint64_t CalculateKeyedNetGroup(const CAddress& ad) const; - - CNode* FindNode(const CNetAddr& ip); - CNode* FindNode(const CSubNet& subNet); - CNode* FindNode(const std::string& addrName); - CNode* FindNode(const CService& addr); - - /** - * Determine whether we're already connected to a given address, in order to - * avoid initiating duplicate connections. - */ - bool AlreadyConnectedToAddress(const CAddress& addr); - - bool AttemptToEvictConnection(); - CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type); - void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const; - - void DeleteNode(CNode* pnode); - - NodeId GetNewNodeId(); - - size_t SocketSendData(CNode *pnode) const; - void DumpAddresses(); - - // Network stats - 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); - - // Network usage totals - RecursiveMutex cs_totalBytesRecv; - RecursiveMutex cs_totalBytesSent; - uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv) {0}; - uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent) {0}; - - // outbound limit & stats - uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent) {0}; - uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent) {0}; - uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); - uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent); - - // P2P timeout in seconds - int64_t m_peer_connect_timeout; - - // Whitelisted ranges. Any node connecting from these is automatically - // whitelisted (as well as those connecting to whitelisted binds). - std::vector<NetWhitelistPermissions> vWhitelistedRange; - - unsigned int nSendBufferMaxSize{0}; - unsigned int nReceiveFloodSize{0}; - - std::vector<ListenSocket> vhListenSocket; - std::atomic<bool> fNetworkActive{true}; - bool fAddressesInitialized{false}; - CAddrMan addrman; - 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); - std::list<CNode*> vNodesDisconnected; - mutable RecursiveMutex cs_vNodes; - std::atomic<NodeId> nLastNodeId{0}; - unsigned int nPrevNodeCount{0}; - - /** - * Cache responses to addr requests to minimize privacy leak. - * Attack example: scraping addrs in real-time may allow an attacker - * to infer new connections of the victim by detecting new records - * with fresh timestamps (per self-announcement). - */ - struct CachedAddrResponse { - std::vector<CAddress> m_addrs_response_cache; - std::chrono::microseconds m_cache_entry_expiration{0}; - }; - - /** - * Addr responses stored in different caches - * 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. - * 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 ~196 KB. Every separate local socket may - * add up to ~196 KB extra. - */ - std::map<uint64_t, CachedAddrResponse> m_addr_response_caches; - - /** - * Services this instance offers. - * - * This data is replicated in each CNode instance we create during peer - * connection (in ConnectNode()) under a member also called - * nLocalServices. - * - * This data is not marked const, but after being set it should not - * change. See the note in CNode::nLocalServices documentation. - * - * \sa CNode::nLocalServices - */ - ServiceFlags nLocalServices; - - std::unique_ptr<CSemaphore> semOutbound; - std::unique_ptr<CSemaphore> semAddnode; - int nMaxConnections; - - // How many full-relay (tx, block, addr) outbound peers we want - int m_max_outbound_full_relay; - - // How many block-relay only outbound peers we want - // We do not relay tx or addr messages with these peers - int m_max_outbound_block_relay; - - int nMaxAddnode; - int nMaxFeeler; - int m_max_outbound; - bool m_use_addrman_outgoing; - std::atomic<int> nBestHeight; - CClientUIInterface* clientInterface; - NetEventsInterface* m_msgproc; - /** 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; - - /** flag for waking the message processor. */ - bool fMsgProcWake GUARDED_BY(mutexMsgProc); - - std::condition_variable condMsgProc; - Mutex mutexMsgProc; - std::atomic<bool> flagInterruptMsgProc{false}; - - CThreadInterrupt interruptNet; - - std::thread threadDNSAddressSeed; - std::thread threadSocketHandler; - std::thread threadOpenAddedConnections; - std::thread threadOpenConnections; - std::thread threadMessageHandler; - - /** flag for deciding to connect to an extra outbound peer, - * in excess of m_max_outbound_full_relay - * This takes the place of a feeler connection */ - std::atomic_bool m_try_another_outbound_peer; - - 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; -}; +/** Convert ConnectionType enum to a string value */ +std::string ConnectionTypeAsString(ConnectionType conn_type); void Discover(); -void StartMapPort(); -void InterruptMapPort(); -void StopMapPort(); uint16_t GetListenPort(); -/** - * Interface for message handling - */ -class NetEventsInterface -{ -public: - virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; - virtual bool SendMessages(CNode* pnode) = 0; - virtual void InitializeNode(CNode* pnode) = 0; - virtual void FinalizeNode(const CNode& node, bool& update_connection_time) = 0; - -protected: - /** - * Protected destructor so that instances can only be deleted by derived classes. - * If that restriction is no longer desired, this should be made public and virtual. - */ - ~NetEventsInterface() = default; -}; - enum { LOCAL_NONE, // unknown LOCAL_IF, // address a local interface listens on LOCAL_BIND, // address explicit bound to - LOCAL_UPNP, // address reported by UPnP + LOCAL_MAPPED, // address reported by UPnP or NAT-PMP LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX @@ -669,7 +215,6 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) extern bool fDiscover; extern bool fListen; -extern bool g_relay_txes; /** Subversion as sent to the P2P network in `version` messages */ extern std::string strSubVersion; @@ -701,14 +246,14 @@ public: int nVersion; std::string cleanSubVer; bool fInbound; - bool m_manual_connection; - int nStartingHeight; + bool m_bip152_highbandwidth_to; + bool m_bip152_highbandwidth_from; + int m_starting_height; uint64_t nSendBytes; mapMsgCmdSize mapSendBytesPerMsgCmd; uint64_t nRecvBytes; mapMsgCmdSize mapRecvBytesPerMsgCmd; NetPermissionFlags m_permissionFlags; - bool m_legacyWhitelisted; int64_t m_ping_usec; int64_t m_ping_wait_usec; int64_t m_min_ping_usec; @@ -719,14 +264,13 @@ 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; + // Network the peer connected through + Network m_network; uint32_t m_mapped_as; - std::string m_conn_type_string; + ConnectionType m_conn_type; }; - /** Transport protocol agnostic message container. * Ideally it should only contain receive time, payload, * command and size. @@ -851,16 +395,18 @@ public: std::unique_ptr<TransportDeserializer> m_deserializer; std::unique_ptr<TransportSerializer> m_serializer; - // socket + NetPermissionFlags m_permissionFlags{PF_NONE}; std::atomic<ServiceFlags> nServices{NODE_NONE}; SOCKET hSocket GUARDED_BY(cs_hSocket); - size_t nSendSize{0}; // total size of all vSendMsg entries - size_t nSendOffset{0}; // offset inside the first vSendMsg already sent + /** Total size of all vSendMsg entries */ + size_t nSendSize GUARDED_BY(cs_vSend){0}; + /** Offset inside the first vSendMsg already sent */ + size_t nSendOffset GUARDED_BY(cs_vSend){0}; uint64_t nSendBytes GUARDED_BY(cs_vSend){0}; std::deque<std::vector<unsigned char>> vSendMsg GUARDED_BY(cs_vSend); - RecursiveMutex cs_vSend; - RecursiveMutex cs_hSocket; - RecursiveMutex cs_vRecv; + Mutex cs_vSend; + Mutex cs_hSocket; + Mutex cs_vRecv; RecursiveMutex cs_vProcessMsg; std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg); @@ -889,8 +435,6 @@ public: bool HasPermission(NetPermissionFlags permission) const { return NetPermissions::HasFlag(m_permissionFlags, permission); } - // This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level - bool m_legacyWhitelisted{false}; bool fClient{false}; // set by version message bool m_limited_node{false}; //after BIP159, set by version message /** @@ -952,6 +496,9 @@ public: /* Whether we send addr messages over this connection */ bool RelayAddrsWithConn() const { + // Don't relay addr messages to peers that we connect to as block-relay-only + // peers (to prevent adversaries from inferring these links from addr + // traffic). return m_conn_type != ConnectionType::BLOCK_RELAY; } @@ -982,13 +529,10 @@ public: */ Network ConnectedThroughNetwork() const; -protected: - mapMsgCmdSize mapSendBytesPerMsgCmd; - mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); - -public: - uint256 hashContinue; - std::atomic<int> nStartingHeight{-1}; + // We selected peer as (compact blocks) high-bandwidth peer (BIP152) + std::atomic<bool> m_bip152_highbandwidth_to{false}; + // Peer selected us as (compact blocks) high-bandwidth peer (BIP152) + std::atomic<bool> m_bip152_highbandwidth_from{false}; // flood relay std::vector<CAddress> vAddrToSend; @@ -997,12 +541,6 @@ public: 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}; - // List of block ids we still have announce. - // There is no final sorting before sending, as they are always sent immediately - // and in the order requested. - std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory); - Mutex cs_inventory; - struct TxRelay { mutable RecursiveMutex cs_filter; // We use fRelayTxes for two purposes - @@ -1020,12 +558,11 @@ public: // Used for BIP35 mempool sending bool fSendMempool GUARDED_BY(cs_tx_inventory){false}; // Last time a "MEMPOOL" request was serviced. - std::atomic<std::chrono::seconds> m_last_mempool_req{std::chrono::seconds{0}}; + std::atomic<std::chrono::seconds> m_last_mempool_req{0s}; std::chrono::microseconds nNextInvSend{0}; - RecursiveMutex cs_feeFilter; - // Minimum fee rate with which to filter inv's to this node - CAmount minFeeFilter GUARDED_BY(cs_feeFilter){0}; + /** Minimum fee rate with which to filter inv's to this node */ + std::atomic<CAmount> minFeeFilter{0}; CAmount lastSentFeeFilter{0}; int64_t nextSendTimeFeeFilter{0}; }; @@ -1033,9 +570,6 @@ public: // m_tx_relay == nullptr if we're not relaying transactions with this peer std::unique_ptr<TxRelay> m_tx_relay; - // Used for headers announcements - unfiltered blocks to relay - std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory); - /** 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 @@ -1053,7 +587,7 @@ public: // The pong reply we're expecting, or 0 if no pong expected. std::atomic<uint64_t> nPingNonceSent{0}; /** When the last ping was sent, or 0 if no ping was ever sent */ - std::atomic<std::chrono::microseconds> m_ping_start{std::chrono::microseconds{0}}; + std::atomic<std::chrono::microseconds> m_ping_start{0us}; // Last measured round-trip time. std::atomic<int64_t> nPingUsecTime{0}; // Best measured round-trip time. @@ -1061,50 +595,11 @@ public: // Whether a ping is requested. std::atomic<bool> fPingQueued{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(NodeId id, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false); ~CNode(); CNode(const CNode&) = delete; CNode& operator=(const CNode&) = delete; -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. - //! - //! This is supplied by the parent CConnman during peer connection - //! (CConnman::ConnectNode()) from its attribute of the same name. - //! - //! This is const because there is no protocol defined for renegotiating - //! services initially offered to a peer. The set of local services we - //! offer should not change after initialization. - //! - //! An interesting example of this is NODE_NETWORK and initial block - //! download: a node which starts up from scratch doesn't have any blocks - //! to serve, but still advertises NODE_NETWORK because it will eventually - //! fulfill this role after IBD completes. P2P code is written in such a - //! way that it can gracefully handle peers who don't make good on their - //! service advertisements. - const ServiceFlags nLocalServices; - - const int nMyStartingHeight; - NetPermissionFlags m_permissionFlags{ PF_NONE }; - std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread - - mutable RecursiveMutex cs_addrName; - std::string addrName GUARDED_BY(cs_addrName); - - // 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 { return id; } @@ -1113,10 +608,6 @@ public: return nLocalHostNonce; } - int GetMyStartingHeight() const { - return nMyStartingHeight; - } - int GetRefCount() const { assert(nRefCount >= 0); @@ -1136,6 +627,7 @@ public: void SetCommonVersion(int greatest_common_version) { + Assume(m_greatest_common_version == INIT_PROTO_VERSION); m_greatest_common_version = greatest_common_version; } int GetCommonVersion() const @@ -1158,26 +650,29 @@ public: nRefCount--; } - - void AddAddressKnown(const CAddress& _addr) { assert(m_addr_known); m_addr_known->insert(_addr.GetKey()); } - void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand) + /** + * Whether the peer supports the address. For example, a peer that does not + * implement BIP155 cannot receive Tor v3 addresses because it requires + * ADDRv2 (BIP155) encoding. + */ + bool IsAddrCompatible(const CAddress& addr) const { - // 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(); + return m_wants_addrv2 || addr.IsAddrV1Compatible(); + } + void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand) + { // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. assert(m_addr_known); - if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) && addr_format_supported) { + if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) && IsAddrCompatible(_addr)) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr; } else { @@ -1186,7 +681,6 @@ public: } } - void AddKnownTx(const uint256& hash) { if (m_tx_relay != nullptr) { @@ -1217,7 +711,525 @@ public: //! Sets the addrName only if it was not previously set void MaybeSetAddrName(const std::string& addrNameIn); - std::string ConnectionTypeAsString() const; + std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); } + + /** Whether this peer is an inbound onion, e.g. connected via our Tor onion service. */ + bool IsInboundOnion() const { return m_inbound_onion; } + +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. + //! + //! This is supplied by the parent CConnman during peer connection + //! (CConnman::ConnectNode()) from its attribute of the same name. + //! + //! This is const because there is no protocol defined for renegotiating + //! services initially offered to a peer. The set of local services we + //! offer should not change after initialization. + //! + //! An interesting example of this is NODE_NETWORK and initial block + //! download: a node which starts up from scratch doesn't have any blocks + //! to serve, but still advertises NODE_NETWORK because it will eventually + //! fulfill this role after IBD completes. P2P code is written in such a + //! way that it can gracefully handle peers who don't make good on their + //! service advertisements. + const ServiceFlags nLocalServices; + + std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread + + mutable RecursiveMutex cs_addrName; + std::string addrName GUARDED_BY(cs_addrName); + + // Our address, as reported by the peer + CService addrLocal GUARDED_BY(cs_addrLocal); + mutable RecursiveMutex cs_addrLocal; + + //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service. + const bool m_inbound_onion{false}; + + mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend); + mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); +}; + +/** + * Interface for message handling + */ +class NetEventsInterface +{ +public: + /** Initialize a peer (setup state, queue any initial messages) */ + virtual void InitializeNode(CNode* pnode) = 0; + + /** Handle removal of a peer (clear state) */ + virtual void FinalizeNode(const CNode& node, bool& update_connection_time) = 0; + + /** + * Process protocol messages received from a given node + * + * @param[in] pnode The node which we have received messages from. + * @param[in] interrupt Interrupt condition for processing threads + * @return True if there is more work to be done + */ + virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + + /** + * Send queued protocol messages to a given node. + * + * @param[in] pnode The node which we are sending messages to. + * @return True if there is more work to be done + */ + virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) = 0; + + +protected: + /** + * Protected destructor so that instances can only be deleted by derived classes. + * If that restriction is no longer desired, this should be made public and virtual. + */ + ~NetEventsInterface() = default; +}; + +class CConnman +{ +public: + + enum NumConnections { + CONNECTIONS_NONE = 0, + CONNECTIONS_IN = (1U << 0), + CONNECTIONS_OUT = (1U << 1), + CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), + }; + + struct Options + { + ServiceFlags nLocalServices = NODE_NONE; + int nMaxConnections = 0; + int m_max_outbound_full_relay = 0; + int m_max_outbound_block_relay = 0; + int nMaxAddnode = 0; + int nMaxFeeler = 0; + CClientUIInterface* uiInterface = nullptr; + NetEventsInterface* m_msgproc = nullptr; + BanMan* m_banman = nullptr; + unsigned int nSendBufferMaxSize = 0; + unsigned int nReceiveFloodSize = 0; + uint64_t nMaxOutboundLimit = 0; + int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT; + std::vector<std::string> vSeedNodes; + 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; + std::vector<bool> m_asmap; + }; + + void Init(const Options& connOptions) { + nLocalServices = connOptions.nLocalServices; + nMaxConnections = connOptions.nMaxConnections; + m_max_outbound_full_relay = std::min(connOptions.m_max_outbound_full_relay, connOptions.nMaxConnections); + m_max_outbound_block_relay = connOptions.m_max_outbound_block_relay; + m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing; + nMaxAddnode = connOptions.nMaxAddnode; + nMaxFeeler = connOptions.nMaxFeeler; + m_max_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + nMaxFeeler; + clientInterface = connOptions.uiInterface; + m_banman = connOptions.m_banman; + m_msgproc = connOptions.m_msgproc; + nSendBufferMaxSize = connOptions.nSendBufferMaxSize; + nReceiveFloodSize = connOptions.nReceiveFloodSize; + m_peer_connect_timeout = connOptions.m_peer_connect_timeout; + { + LOCK(cs_totalBytesSent); + nMaxOutboundLimit = connOptions.nMaxOutboundLimit; + } + vWhitelistedRange = connOptions.vWhitelistedRange; + { + 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); + ~CConnman(); + bool Start(CScheduler& scheduler, const Options& options); + + void StopThreads(); + void StopNodes(); + void Stop() + { + StopThreads(); + StopNodes(); + }; + + void Interrupt(); + 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, 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); + + using NodeFn = std::function<void(CNode*)>; + void ForEachNode(const NodeFn& func) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + func(node); + } + }; + + void ForEachNode(const NodeFn& func) const + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + func(node); + } + }; + + template<typename Callable, typename CallableAfter> + void ForEachNodeThen(Callable&& pre, CallableAfter&& post) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + pre(node); + } + post(); + }; + + template<typename Callable, typename CallableAfter> + void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + pre(node); + } + post(); + }; + + // Addrman functions + void SetServices(const CService &addr, ServiceFlags nServices); + void MarkAddressGood(const CAddress& addr); + 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(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. + void SetTryNewOutboundPeer(bool flag); + bool GetTryNewOutboundPeer(); + + void StartExtraBlockRelayPeers() { + LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n"); + m_start_extra_block_relay_peers = true; + } + + // Return the number of outbound peers we have in excess of our target (eg, + // if we previously called SetTryNewOutboundPeer(true), and have since set + // to false, we may have extra peers that we wish to disconnect). This may + // return a value less than (num_outbound_connections - num_outbound_slots) + // in cases where some outbound connections are not yet fully connected, or + // not yet fully disconnected. + int GetExtraFullOutboundCount(); + // Count the number of block-relay-only peers we have over our limit. + int GetExtraBlockRelayCount(); + + bool AddNode(const std::string& node); + bool RemoveAddedNode(const std::string& node); + std::vector<AddedNodeInfo> GetAddedNodeInfo(); + + /** + * Attempts to open a connection. Currently only used from tests. + * + * @param[in] address Address of node to try connecting to + * @param[in] conn_type ConnectionType::OUTBOUND or ConnectionType::BLOCK_RELAY + * @return bool Returns false if there are no available + * slots for this connection: + * - conn_type not a supported ConnectionType + * - Max total outbound connection capacity filled + * - Max connection capacity for type is filled + */ + bool AddConnection(const std::string& address, ConnectionType conn_type); + + size_t GetNodeCount(NumConnections num); + void GetNodeStats(std::vector<CNodeStats>& vstats); + bool DisconnectNode(const std::string& node); + bool DisconnectNode(const CSubNet& subnet); + bool DisconnectNode(const CNetAddr& addr); + bool DisconnectNode(NodeId id); + + //! Used to convey which local services we are offering peers during node + //! connection. + //! + //! The data returned by this is used in CNode construction, + //! which is used to advertise which services we are offering + //! that peer during `net_processing.cpp:PushNodeVersion()`. + ServiceFlags GetLocalServices() const; + + uint64_t GetMaxOutboundTarget(); + std::chrono::seconds GetMaxOutboundTimeframe(); + + //! check if the outbound target is reached + //! if param historicalBlockServingLimit is set true, the function will + //! response true if the limit for serving historical blocks has been reached + bool OutboundTargetReached(bool historicalBlockServingLimit); + + //! response the bytes left in the current max outbound cycle + //! in case of no limit, it will always response 0 + uint64_t GetOutboundTargetBytesLeft(); + + //! returns the time left in the current max outbound cycle + //! in case of no limit, it will always return 0 + std::chrono::seconds GetMaxOutboundTimeLeftInCycle(); + + uint64_t GetTotalBytesRecv(); + uint64_t GetTotalBytesSent(); + + /** Get a unique deterministic randomizer. */ + CSipHasher GetDeterministicRandomizer(uint64_t id) const; + + unsigned int GetReceiveFloodSize() const; + + void WakeMessageHandler(); + + /** Attempts to obfuscate tx time through exponentially distributed emitting. + Works assuming that a single interval is used. + Variable intervals will result in privacy decrease. + */ + int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds); + + void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); } + +private: + struct ListenSocket { + public: + SOCKET socket; + inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); } + ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {} + private: + NetPermissionFlags m_permissions; + }; + + bool BindListenPort(const CService& bindAddr, 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, + const std::vector<CService>& onion_binds); + + void ThreadOpenAddedConnections(); + void AddAddrFetch(const std::string& strDest); + void ProcessAddrFetch(); + void ThreadOpenConnections(std::vector<std::string> connect); + void ThreadMessageHandler(); + void AcceptConnection(const ListenSocket& hListenSocket); + void DisconnectNodes(); + void NotifyNumConnectionsChanged(); + /** Return true if the peer is inactive and should be disconnected. */ + bool InactivityCheck(const CNode& node) const; + bool GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set); + void SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set); + void SocketHandler(); + void ThreadSocketHandler(); + void ThreadDNSAddressSeed(); + + uint64_t CalculateKeyedNetGroup(const CAddress& ad) const; + + CNode* FindNode(const CNetAddr& ip); + CNode* FindNode(const CSubNet& subNet); + CNode* FindNode(const std::string& addrName); + CNode* FindNode(const CService& addr); + + /** + * Determine whether we're already connected to a given address, in order to + * avoid initiating duplicate connections. + */ + bool AlreadyConnectedToAddress(const CAddress& addr); + + bool AttemptToEvictConnection(); + CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type); + void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const; + + void DeleteNode(CNode* pnode); + + NodeId GetNewNodeId(); + + size_t SocketSendData(CNode& node) const EXCLUSIVE_LOCKS_REQUIRED(node.cs_vSend); + void DumpAddresses(); + + // Network stats + 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); + + // Network usage totals + RecursiveMutex cs_totalBytesRecv; + RecursiveMutex cs_totalBytesSent; + uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv) {0}; + uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent) {0}; + + // outbound limit & stats + uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent) {0}; + std::chrono::seconds nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent) {0}; + uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); + + // P2P timeout in seconds + int64_t m_peer_connect_timeout; + + // Whitelisted ranges. Any node connecting from these is automatically + // whitelisted (as well as those connecting to whitelisted binds). + std::vector<NetWhitelistPermissions> vWhitelistedRange; + + unsigned int nSendBufferMaxSize{0}; + unsigned int nReceiveFloodSize{0}; + + std::vector<ListenSocket> vhListenSocket; + std::atomic<bool> fNetworkActive{true}; + bool fAddressesInitialized{false}; + CAddrMan addrman; + 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); + std::list<CNode*> vNodesDisconnected; + mutable RecursiveMutex cs_vNodes; + std::atomic<NodeId> nLastNodeId{0}; + unsigned int nPrevNodeCount{0}; + + /** + * Cache responses to addr requests to minimize privacy leak. + * Attack example: scraping addrs in real-time may allow an attacker + * to infer new connections of the victim by detecting new records + * with fresh timestamps (per self-announcement). + */ + struct CachedAddrResponse { + std::vector<CAddress> m_addrs_response_cache; + std::chrono::microseconds m_cache_entry_expiration{0}; + }; + + /** + * Addr responses stored in different caches + * 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. + * 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 ~196 KB. Every separate local socket may + * add up to ~196 KB extra. + */ + std::map<uint64_t, CachedAddrResponse> m_addr_response_caches; + + /** + * Services this instance offers. + * + * This data is replicated in each CNode instance we create during peer + * connection (in ConnectNode()) under a member also called + * nLocalServices. + * + * This data is not marked const, but after being set it should not + * change. See the note in CNode::nLocalServices documentation. + * + * \sa CNode::nLocalServices + */ + ServiceFlags nLocalServices; + + std::unique_ptr<CSemaphore> semOutbound; + std::unique_ptr<CSemaphore> semAddnode; + int nMaxConnections; + + // How many full-relay (tx, block, addr) outbound peers we want + int m_max_outbound_full_relay; + + // How many block-relay only outbound peers we want + // We do not relay tx or addr messages with these peers + int m_max_outbound_block_relay; + + int nMaxAddnode; + int nMaxFeeler; + int m_max_outbound; + bool m_use_addrman_outgoing; + CClientUIInterface* clientInterface; + NetEventsInterface* m_msgproc; + /** 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; + + /** flag for waking the message processor. */ + bool fMsgProcWake GUARDED_BY(mutexMsgProc); + + std::condition_variable condMsgProc; + Mutex mutexMsgProc; + std::atomic<bool> flagInterruptMsgProc{false}; + + CThreadInterrupt interruptNet; + + std::thread threadDNSAddressSeed; + std::thread threadSocketHandler; + std::thread threadOpenAddedConnections; + std::thread threadOpenConnections; + std::thread threadMessageHandler; + + /** flag for deciding to connect to an extra outbound peer, + * in excess of m_max_outbound_full_relay + * This takes the place of a feeler connection */ + std::atomic_bool m_try_another_outbound_peer; + + /** flag for initiating extra block-relay-only peer connections. + * this should only be enabled after initial chain sync has occurred, + * as these connections are intended to be short-lived and low-bandwidth. + */ + std::atomic_bool m_start_extra_block_relay_peers{false}; + + 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; }; /** Return a timestamp in the future (in microseconds) for exponentially distributed events. */ @@ -1229,4 +1241,21 @@ inline std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, return std::chrono::microseconds{PoissonNextSend(now.count(), average_interval.count())}; } +struct NodeEvictionCandidate +{ + NodeId id; + int64_t nTimeConnected; + int64_t nMinPingUsecTime; + int64_t nLastBlockTime; + int64_t nLastTXTime; + bool fRelevantServices; + bool fRelayTxes; + bool fBloomFilter; + uint64_t nKeyedNetGroup; + bool prefer_evict; + bool m_is_local; +}; + +[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates); + #endif // BITCOIN_NET_H diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp index d40fdfb113..1fdcd97593 100644 --- a/src/net_permissions.cpp +++ b/src/net_permissions.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/net_processing.cpp b/src/net_processing.cpp index e9915a3091..b68453759a 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -26,6 +26,7 @@ #include <streams.h> #include <tinyformat.h> #include <txmempool.h> +#include <txrequest.h> #include <util/check.h> // For NDEBUG compile time check #include <util/strencodings.h> #include <util/system.h> @@ -168,6 +169,186 @@ void EraseOrphansFor(NodeId peer); // Internal stuff namespace { +/** + * 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. + * + * Mutexes inside this struct must not be held when locking m_peer_mutex. + * + * 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}; + + /** Protects block inventory data members */ + Mutex m_block_inv_mutex; + /** List of blocks that we'll announce via an `inv` message. + * There is no final sorting before sending, as they are always sent + * immediately and in the order requested. */ + std::vector<uint256> m_blocks_for_inv_relay GUARDED_BY(m_block_inv_mutex); + /** Unfiltered list of blocks that we'd like to announce via a `headers` + * message. If we can't announce via a `headers` message, we'll fall back to + * announcing via `inv`. */ + std::vector<uint256> m_blocks_for_headers_relay GUARDED_BY(m_block_inv_mutex); + /** The final block hash that we sent in an `inv` message to this peer. + * When the peer requests this block, we send an `inv` message to trigger + * the peer to request the next sequence of block hashes. + * Most peers use headers-first syncing, which doesn't use this mechanism */ + uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {}; + + /** This peer's reported block height when we connected */ + std::atomic<int> m_starting_height{-1}; + + /** 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); + + explicit Peer(NodeId id) : m_id(id) {} +}; + +using PeerRef = std::shared_ptr<Peer>; + +class PeerManagerImpl final : public PeerManager +{ +public: + PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs); + + /** Overridden from CValidationInterface. */ + void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override; + void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override; + void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; + void BlockChecked(const CBlock& block, const BlockValidationState& state) override; + void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; + + /** Implement NetEventsInterface */ + void InitializeNode(CNode* pnode) override; + void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override; + bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; + bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); + + /** Implement PeerManager */ + void CheckForStaleTipAndEvictPeers() override; + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override; + bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } + void SetBestHeight(int height) override { m_best_height = height; }; + void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override; + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override; + +private: + /** 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); + + /** 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; + + /** Get a shared pointer to the Peer object. + * May return an empty shared_ptr if the Peer object can't be found. */ + PeerRef GetPeerRef(NodeId id) const; + + /** Get a shared pointer to the Peer object and remove it from m_peer_map. + * May return an empty shared_ptr if the Peer object can't be found. */ + PeerRef RemovePeer(NodeId id); + + /** + * 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 Peer& peer, + 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); + + /** Send a version message to a peer */ + void PushNodeVersion(CNode& pnode, int64_t nTime); + + 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); + + /** The height of the best chain */ + std::atomic<int> m_best_height{-1}; + + int64_t m_stale_tip_check_time; //!< Next time to check for stale tip + + /** Whether this node is running in blocks only mode */ + const bool m_ignore_incoming_txs; + + /** Whether we've completed initial sync yet, for determining when to turn + * on extra block-relay-only peers. */ + bool m_initial_sync_finished{false}; + + /** Protects m_peer_map. This mutex must not be locked while holding a lock + * on any of the mutexes inside a Peer object. */ + mutable Mutex m_peer_mutex; + /** + * Map of all Peer objects, keyed by peer id. This map is protected + * by the m_peer_mutex. Once a shared pointer reference is + * taken, the lock may be released. Individual fields are protected by + * their own locks. + */ + std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex); +}; +} // namespace + +namespace { /** Number of nodes with fSyncStarted. */ int nSyncStarted GUARDED_BY(cs_main) = 0; @@ -376,17 +557,14 @@ struct CNodeState { //! Whether this peer is an inbound connection bool m_is_inbound; - //! Whether this peer is a manual connection - bool m_is_manual_connection; - //! A rolling bloom filter of all announced tx CInvs to this peer. CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001}; //! Whether this peer relays txs via wtxid bool m_wtxid_relay{false}; - CNodeState(CAddress addrIn, bool is_inbound, bool is_manual) - : address(addrIn), m_is_inbound(is_inbound), m_is_manual_connection(is_manual) + CNodeState(CAddress addrIn, bool is_inbound) + : address(addrIn), m_is_inbound(is_inbound) { pindexBestKnownBlock = nullptr; hashLastUnknownBlock.SetNull(); @@ -422,58 +600,6 @@ 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; @@ -484,32 +610,6 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS nPreferredDownload += state->fPreferredDownload; } -static void PushNodeVersion(CNode& pnode, CConnman& connman, int64_t nTime) -{ - // Note that pnode->GetLocalServices() is a reflection of the local - // services we were offering when the CNode object was created for this - // peer. - ServiceFlags nLocalNodeServices = pnode.GetLocalServices(); - uint64_t nonce = pnode.GetLocalNonce(); - int nNodeStartingHeight = pnode.GetMyStartingHeight(); - NodeId nodeid = pnode.GetId(); - CAddress addr = pnode.addr; - - CAddress addrYou = addr.IsRoutable() && !IsProxy(addr) && addr.IsAddrV1Compatible() ? - addr : - CAddress(CService(), addr.nServices); - CAddress addrMe = CAddress(CService(), nLocalNodeServices); - - connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, - nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode.m_tx_relay != nullptr)); - - if (fLogIPs) { - LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid); - } else { - LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), nodeid); - } -} - // Returns a bool indicating whether we requested this block. // Also used if a block was /not/ received and timed out or started with another peer static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { @@ -576,7 +676,7 @@ static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_ assert(state != nullptr); if (!state->hashLastUnknownBlock.IsNull()) { - const CBlockIndex* pindex = LookupBlockIndex(state->hashLastUnknownBlock); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock); if (pindex && pindex->nChainWork > 0) { if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { state->pindexBestKnownBlock = pindex; @@ -593,7 +693,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV ProcessBlockAvailability(nodeid); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (pindex && pindex->nChainWork > 0) { // An actually better block was announced. if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { @@ -635,11 +735,15 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma // blocks using compact encodings. connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){ connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion)); + // save BIP152 bandwidth state: we select peer to be low-bandwidth + pnodeStop->m_bip152_highbandwidth_to = false; return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion)); + // save BIP152 bandwidth state: we select peer to be high-bandwidth + pfrom->m_bip152_highbandwidth_to = true; lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); @@ -760,7 +864,34 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec } // namespace -void PeerManager::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) +void PeerManagerImpl::PushNodeVersion(CNode& pnode, int64_t nTime) +{ + // Note that pnode->GetLocalServices() is a reflection of the local + // services we were offering when the CNode object was created for this + // peer. + ServiceFlags nLocalNodeServices = pnode.GetLocalServices(); + uint64_t nonce = pnode.GetLocalNonce(); + const int nNodeStartingHeight{m_best_height}; + NodeId nodeid = pnode.GetId(); + CAddress addr = pnode.addr; + + CAddress addrYou = addr.IsRoutable() && !IsProxy(addr) && addr.IsAddrV1Compatible() ? + addr : + CAddress(CService(), addr.nServices); + CAddress addrMe = CAddress(CService(), nLocalNodeServices); + + const bool tx_relay = !m_ignore_incoming_txs && pnode.m_tx_relay != nullptr; + m_connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, + nonce, strSubVersion, nNodeStartingHeight, tx_relay)); + + if (fLogIPs) { + LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, txrelay=%d, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), tx_relay, nodeid); + } else { + LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, txrelay=%d, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), tx_relay, nodeid); + } +} + +void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) { AssertLockHeld(::cs_main); // For m_txrequest NodeId nodeid = node.GetId(); @@ -796,26 +927,27 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) if (state) state->m_last_block_announcement = time_in_seconds; } -void PeerManager::InitializeNode(CNode *pnode) { +void PeerManagerImpl::InitializeNode(CNode *pnode) +{ CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); NodeId nodeid = pnode->GetId(); { LOCK(cs_main); - mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn(), pnode->IsManualConn())); + mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn())); 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)); + LOCK(m_peer_mutex); + m_peer_map.emplace_hint(m_peer_map.end(), nodeid, std::move(peer)); } if (!pnode->IsInboundConn()) { - PushNodeVersion(*pnode, m_connman, GetTime()); + PushNodeVersion(*pnode, GetTime()); } } -void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const +void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) const { std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs(); @@ -836,17 +968,21 @@ void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } -void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) { +void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) +{ NodeId nodeid = node.GetId(); fUpdateConnectionTime = false; LOCK(cs_main); int misbehavior{0}; { - PeerRef peer = GetPeerRef(nodeid); + // We remove the PeerRef from g_peer_map here, but we don't always + // destruct the Peer. Sometimes another thread is still holding a + // PeerRef, so the refcount is >= 1. Be careful not to do any + // processing here that assumes Peer won't be changed before it's + // destructed. + PeerRef peer = RemovePeer(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); @@ -887,7 +1023,27 @@ void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) { LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); } -bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { +PeerRef PeerManagerImpl::GetPeerRef(NodeId id) const +{ + LOCK(m_peer_mutex); + auto it = m_peer_map.find(id); + return it != m_peer_map.end() ? it->second : nullptr; +} + +PeerRef PeerManagerImpl::RemovePeer(NodeId id) +{ + PeerRef ret; + LOCK(m_peer_mutex); + auto it = m_peer_map.find(id); + if (it != m_peer_map.end()) { + ret = std::move(it->second); + m_peer_map.erase(it); + } + return ret; +} + +bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) +{ { LOCK(cs_main); CNodeState* state = State(nodeid); @@ -903,7 +1059,7 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { PeerRef peer = GetPeerRef(nodeid); if (peer == nullptr) return false; - stats.m_misbehavior_score = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score); + stats.m_starting_height = peer->m_starting_height; return true; } @@ -1044,7 +1200,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) return nEvicted; } -void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) +void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) { assert(howmuch > 0); @@ -1062,8 +1218,8 @@ void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std:: } } -bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, - bool via_compact_block, const std::string& message) +bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, + bool via_compact_block, const std::string& message) { switch (state.GetResult()) { case BlockValidationResult::BLOCK_RESULT_UNSET: @@ -1085,8 +1241,8 @@ bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationSt } // Discourage outbound (but not inbound) peers if on an invalid chain. - // Exempt HB compact block peers and manual connections. - if (!via_compact_block && !node_state->m_is_inbound && !node_state->m_is_manual_connection) { + // Exempt HB compact block peers. Manual connections are always protected from discouragement. + if (!via_compact_block && !node_state->m_is_inbound) { Misbehaving(nodeid, 100, message); return true; } @@ -1112,7 +1268,7 @@ bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationSt return false; } -bool PeerManager::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) +bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) { switch (state.GetResult()) { case TxValidationResult::TX_RESULT_UNSET: @@ -1158,14 +1314,23 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); } -PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, - CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool) +std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs) +{ + return std::make_unique<PeerManagerImpl>(chainparams, connman, banman, scheduler, chainman, pool, ignore_incoming_txs); +} + +PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs) : m_chainparams(chainparams), m_connman(connman), m_banman(banman), m_chainman(chainman), m_mempool(pool), - m_stale_tip_check_time(0) + m_stale_tip_check_time(0), + m_ignore_incoming_txs(ignore_incoming_txs) { // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); @@ -1198,7 +1363,7 @@ PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, Ban * block, remember the recently confirmed transactions, and delete tracked * announcements for them. Also save the time of the last tip update. */ -void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex) +void PeerManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex) { { LOCK(g_cs_orphans); @@ -1249,7 +1414,7 @@ void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, co } } -void PeerManager::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) +void PeerManagerImpl::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 @@ -1274,7 +1439,8 @@ 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 PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { +void PeerManagerImpl::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); @@ -1321,42 +1487,47 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ * Update our best height and announce any block hashes which weren't previously * in ::ChainActive() to our peers. */ -void PeerManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { - const int nNewHeight = pindexNew->nHeight; - m_connman.SetBestHeight(nNewHeight); - +void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) +{ + SetBestHeight(pindexNew->nHeight); SetServiceFlagsIBDCache(!fInitialDownload); - if (!fInitialDownload) { - // Find the hashes of all blocks that weren't previously in the best chain. - std::vector<uint256> vHashes; - const CBlockIndex *pindexToAnnounce = pindexNew; - while (pindexToAnnounce != pindexFork) { - vHashes.push_back(pindexToAnnounce->GetBlockHash()); - pindexToAnnounce = pindexToAnnounce->pprev; - if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { - // Limit announcements in case of a huge reorganization. - // Rely on the peer's synchronization mechanism in that case. - break; - } + + // Don't relay inventory during initial block download. + if (fInitialDownload) return; + + // Find the hashes of all blocks that weren't previously in the best chain. + std::vector<uint256> vHashes; + const CBlockIndex *pindexToAnnounce = pindexNew; + while (pindexToAnnounce != pindexFork) { + vHashes.push_back(pindexToAnnounce->GetBlockHash()); + pindexToAnnounce = pindexToAnnounce->pprev; + if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { + // Limit announcements in case of a huge reorganization. + // Rely on the peer's synchronization mechanism in that case. + break; } - // Relay inventory, but don't relay old inventory during initial block download. - 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)) { - pnode->vBlockHashesToAnnounce.push_back(hash); - } + } + + { + LOCK(m_peer_mutex); + for (auto& it : m_peer_map) { + Peer& peer = *it.second; + LOCK(peer.m_block_inv_mutex); + for (const uint256& hash : reverse_iterate(vHashes)) { + peer.m_blocks_for_headers_relay.push_back(hash); } - }); - m_connman.WakeMessageHandler(); + } } + + m_connman.WakeMessageHandler(); } /** * Handle invalid block rejection and consequent peer discouragement, maintain which * peers announce compact blocks. */ -void PeerManager::BlockChecked(const CBlock& block, const BlockValidationState& state) { +void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationState& state) +{ LOCK(cs_main); const uint256 hash(block.GetHash()); @@ -1425,7 +1596,7 @@ bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLU bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { - return LookupBlockIndex(block_hash) != nullptr; + return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr; } void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) @@ -1443,7 +1614,23 @@ void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& }); } -static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& connman) +/** + * Relay (gossip) an address to a few randomly chosen nodes. + * We choose the same nodes within a given 24h window (if the list of connected + * nodes does not change) and we don't relay to nodes that already know an + * address. So within 24h we will likely relay a given address once. This is to + * prevent a peer from unjustly giving their address better propagation by sending + * it to us repeatedly. + * @param[in] originator The peer that sent us the address. We don't want to relay it back. + * @param[in] addr Address to relay. + * @param[in] fReachable Whether the address' network is reachable. We relay unreachable + * addresses less. + * @param[in] connman Connection manager to choose nodes to relay to. + */ +static void RelayAddress(const CNode& originator, + const CAddress& addr, + bool fReachable, + const CConnman& connman) { if (!fReachable && !addr.IsRelayable()) return; @@ -1460,8 +1647,8 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& 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->RelayAddrsWithConn()) { + auto sortfunc = [&best, &hasher, nRelayNodes, &originator, &addr](CNode* pnode) { + if (pnode->RelayAddrsWithConn() && pnode != &originator && pnode->IsAddrCompatible(addr)) { uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize(); for (unsigned int i = 0; i < nRelayNodes; i++) { if (hashKey > best[i].first) { @@ -1482,7 +1669,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, const CInv& inv, CConnman& connman) +void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman) { bool send = false; std::shared_ptr<const CBlock> a_recent_block; @@ -1499,7 +1686,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c bool need_activate_chain = false; { LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(inv.hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) && pindex->IsValid(BLOCK_VALID_TREE)) { @@ -1514,13 +1701,13 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c } // release cs_main before calling ActivateBestChain if (need_activate_chain) { BlockValidationState state; - if (!ActivateBestChain(state, chainparams, a_recent_block)) { + if (!::ChainstateActive().ActivateBestChain(state, chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(inv.hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { send = BlockRequestAllowed(pindex, consensusParams); if (!send) { @@ -1622,16 +1809,18 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c } } - // Trigger the peer node to send a getblocks request for the next batch of inventory - if (inv.hash == pfrom.hashContinue) { - // Send immediately. This must send even if redundant, - // and we want it right after the last block so they don't - // wait for other stuff first. - std::vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash())); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); - pfrom.hashContinue.SetNull(); + LOCK(peer.m_block_inv_mutex); + // Trigger the peer node to send a getblocks request for the next batch of inventory + if (inv.hash == peer.m_continuation_block) { + // Send immediately. This must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + std::vector<CInv> vInv; + vInv.push_back(CInv(MSG_BLOCK, ::ChainActive().Tip()->GetBlockHash())); + connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); + peer.m_continuation_block.SetNull(); + } } } } @@ -1731,7 +1920,7 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) { const CInv &inv = *it++; if (inv.IsGenBlkMsg()) { - ProcessGetBlockData(pfrom, chainparams, inv, connman); + ProcessGetBlockData(pfrom, peer, 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. @@ -1766,7 +1955,8 @@ static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_ma return nFetchFlags; } -void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req) { +void PeerManagerImpl::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()) { @@ -1781,7 +1971,9 @@ void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } -void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block) +void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, + const std::vector<CBlockHeader>& headers, + bool via_compact_block) { const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); size_t nCount = headers.size(); @@ -1805,7 +1997,7 @@ void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHe // don't connect before giving DoS points // - Once a headers message is received that is valid and does connect, // nUnconnectingHeaders gets reset back to 0. - if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + if (!g_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", @@ -1835,7 +2027,7 @@ void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHe // If we don't have the last header, then they'll have given us // something new (if these headers are valid). - if (!LookupBlockIndex(hashLastBlock)) { + if (!g_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) { received_new_header = true; } } @@ -1871,7 +2063,8 @@ void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHe // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue // from there instead. - LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight); + LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", + pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height); m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256())); } @@ -1972,7 +2165,7 @@ void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHe * may be added to if accepting an orphan causes its children to be * reconsidered. */ -void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set) +void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) { AssertLockHeld(cs_main); AssertLockHeld(g_cs_orphans); @@ -2086,7 +2279,7 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par { LOCK(cs_main); - stop_index = LookupBlockIndex(stop_hash); + stop_index = g_chainman.m_blockman.LookupBlockIndex(stop_hash); // Check that the stop block exists and the peer would be allowed to fetch it. if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) { @@ -2269,25 +2462,18 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar connman.PushMessage(&peer, std::move(msg)); } -void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, - const std::chrono::microseconds time_received, - const std::atomic<bool>& interruptMsgProc) +void PeerManagerImpl::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) - { - LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); - 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) - { - Misbehaving(pfrom.GetId(), 1, "redundant version message"); + if (pfrom.nVersion != 0) { + LogPrint(BCLog::NET, "redundant version message from peer=%d\n", pfrom.GetId()); return; } @@ -2299,7 +2485,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat ServiceFlags nServices; int nVersion; std::string cleanSubVer; - int nStartingHeight = -1; + int starting_height = -1; bool fRelay = true; vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; @@ -2330,7 +2516,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat cleanSubVer = SanitizeString(strSubVer); } if (!vRecv.empty()) { - vRecv >> nStartingHeight; + vRecv >> starting_height; } if (!vRecv.empty()) vRecv >> fRelay; @@ -2347,9 +2533,9 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat SeenLocal(addrMe); } - // Be shy and don't send version until we hear - if (pfrom.IsInboundConn()) - PushNodeVersion(pfrom, m_connman, GetAdjustedTime()); + // Inbound peers send us their version message when they connect. + // We send our version message in response. + if (pfrom.IsInboundConn()) PushNodeVersion(pfrom, GetAdjustedTime()); // Change version const int greatest_common_version = std::min(nVersion, PROTOCOL_VERSION); @@ -2362,10 +2548,16 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::WTXIDRELAY)); } - m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK)); - // Signal ADDRv2 support (BIP155). - m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2)); + if (greatest_common_version >= 70016) { + // BIP155 defines addrv2 and sendaddrv2 for all protocol versions, but some + // implementations reject messages they don't know. As a courtesy, don't send + // it to nodes with a version before 70016, as no software is known to support + // BIP155 that doesn't announce at least that protocol version number. + m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2)); + } + + m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK)); pfrom.nServices = nServices; pfrom.SetAddrLocal(addrMe); @@ -2373,7 +2565,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat LOCK(pfrom.cs_SubVer); pfrom.cleanSubVer = cleanSubVer; } - pfrom.nStartingHeight = nStartingHeight; + peer->m_starting_height = starting_height; // set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients" pfrom.fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED)); @@ -2407,8 +2599,8 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat // empty and no one will know who we are, so these mechanisms are // important to help us connect to the network. // - // We skip this for BLOCK_RELAY peers to avoid potentially leaking - // information about our BLOCK_RELAY connections via address relay. + // We skip this for block-relay-only peers to avoid potentially leaking + // information about our block-relay-only connections via address relay. if (fListen && !::ChainstateActive().IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices()); @@ -2451,9 +2643,9 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (fLogIPs) remoteAddr = ", peeraddr=" + pfrom.addr.ToString(); - LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", + LogPrint(BCLog::NET, "receive version message: %s: version %d, blocks=%d, us=%s, txrelay=%d, peer=%d%s\n", cleanSubVer, pfrom.nVersion, - pfrom.nStartingHeight, addrMe.ToString(), pfrom.GetId(), + peer->m_starting_height, addrMe.ToString(), fRelay, pfrom.GetId(), remoteAddr); int64_t nTimeOffset = nTime - GetTime(); @@ -2468,6 +2660,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat // Feeler connections exist only to verify if address is online. if (pfrom.IsFeelerConn()) { + LogPrint(BCLog::NET, "feeler connection completed peer=%d; disconnecting\n", pfrom.GetId()); pfrom.fDisconnect = true; } return; @@ -2475,7 +2668,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (pfrom.nVersion == 0) { // Must have a version message before anything else - Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake"); + LogPrint(BCLog::NET, "non-version message before version handshake. Message \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId()); return; } @@ -2483,13 +2676,16 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); if (msg_type == NetMsgType::VERACK) { - if (pfrom.fSuccessfullyConnected) return; + if (pfrom.fSuccessfullyConnected) { + LogPrint(BCLog::NET, "ignoring redundant verack message from peer=%d\n", pfrom.GetId()); + return; + } if (!pfrom.IsInboundConn()) { LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n", - pfrom.nVersion.load(), pfrom.nStartingHeight, + pfrom.nVersion.load(), peer->m_starting_height, pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) : ""), - pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay"); + pfrom.ConnectionTypeAsString()); } if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) { @@ -2516,12 +2712,46 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat return; } - // Feature negotiation of wtxidrelay should happen between VERSION and - // VERACK, to avoid relay problems from switching after a connection is up + if (msg_type == NetMsgType::SENDHEADERS) { + LOCK(cs_main); + State(pfrom.GetId())->fPreferHeaders = true; + return; + } + + if (msg_type == NetMsgType::SENDCMPCT) { + bool fAnnounceUsingCMPCTBLOCK = false; + uint64_t nCMPCTBLOCKVersion = 0; + vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; + if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) { + LOCK(cs_main); + // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness) + if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) { + State(pfrom.GetId())->fProvidesHeaderAndIDs = true; + State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2; + } + if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) { // ignore later version announces + State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK; + // save whether peer selects us as BIP152 high-bandwidth peer + // (receiving sendcmpct(1) signals high-bandwidth, sendcmpct(0) low-bandwidth) + pfrom.m_bip152_highbandwidth_from = fAnnounceUsingCMPCTBLOCK; + } + if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) { + if (pfrom.GetLocalServices() & NODE_WITNESS) + State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2); + else + State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1); + } + } + return; + } + + // Feature negotiation of wtxidrelay must happen between VERSION and VERACK + // to avoid relay problems from switching after a connection is up. if (msg_type == NetMsgType::WTXIDRELAY) { if (pfrom.fSuccessfullyConnected) { // Disconnect peers that send wtxidrelay message after VERACK; this // must be negotiated between VERSION and VERACK. + LogPrint(BCLog::NET, "wtxidrelay received after verack from peer=%d; disconnecting\n", pfrom.GetId()); pfrom.fDisconnect = true; return; } @@ -2530,11 +2760,27 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (!State(pfrom.GetId())->m_wtxid_relay) { State(pfrom.GetId())->m_wtxid_relay = true; g_wtxid_relay_peers++; + } else { + LogPrint(BCLog::NET, "ignoring duplicate wtxidrelay from peer=%d\n", pfrom.GetId()); } + } else { + LogPrint(BCLog::NET, "ignoring wtxidrelay due to old common version=%d from peer=%d\n", pfrom.GetCommonVersion(), pfrom.GetId()); } return; } + if (msg_type == NetMsgType::SENDADDRV2) { + if (pfrom.fSuccessfullyConnected) { + // Disconnect peers that send SENDADDRV2 message after VERACK; this + // must be negotiated between VERSION and VERACK. + LogPrint(BCLog::NET, "sendaddrv2 received after verack from peer=%d; disconnecting\n", pfrom.GetId()); + pfrom.fDisconnect = true; + return; + } + pfrom.m_wants_addrv2 = true; + return; + } + if (!pfrom.fSuccessfullyConnected) { LogPrint(BCLog::NET, "Unsupported message \"%s\" prior to verack from peer=%d\n", SanitizeString(msg_type), pfrom.GetId()); return; @@ -2554,6 +2800,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat s >> vAddr; if (!pfrom.RelayAddrsWithConn()) { + LogPrint(BCLog::NET, "ignoring %s message from %s peer=%d\n", msg_type, pfrom.ConnectionTypeAsString(), pfrom.GetId()); return; } if (vAddr.size() > MAX_ADDR_TO_SEND) @@ -2588,7 +2835,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes - RelayAddress(addr, fReachable, m_connman); + RelayAddress(pfrom, addr, fReachable, m_connman); } // Do not store addresses outside our network if (fReachable) @@ -2597,41 +2844,9 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat m_connman.AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom.fGetAddr = false; - if (pfrom.IsAddrFetchConn()) + if (pfrom.IsAddrFetchConn()) { + LogPrint(BCLog::NET, "addrfetch connection completed peer=%d; disconnecting\n", pfrom.GetId()); 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; - return; - } - - if (msg_type == NetMsgType::SENDCMPCT) { - bool fAnnounceUsingCMPCTBLOCK = false; - uint64_t nCMPCTBLOCKVersion = 0; - vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; - if (nCMPCTBLOCKVersion == 1 || ((pfrom.GetLocalServices() & NODE_WITNESS) && nCMPCTBLOCKVersion == 2)) { - LOCK(cs_main); - // fProvidesHeaderAndIDs is used to "lock in" version of compact blocks we send (fWantsCmpctWitness) - if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) { - State(pfrom.GetId())->fProvidesHeaderAndIDs = true; - State(pfrom.GetId())->fWantsCmpctWitness = nCMPCTBLOCKVersion == 2; - } - if (State(pfrom.GetId())->fWantsCmpctWitness == (nCMPCTBLOCKVersion == 2)) // ignore later version announces - State(pfrom.GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK; - if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) { - if (pfrom.GetLocalServices() & NODE_WITNESS) - State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 2); - else - State(pfrom.GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1); - } } return; } @@ -2647,7 +2862,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat // We won't accept tx inv's if we're in blocks-only mode, or this is a // block-relay-only peer - bool fBlocksOnly = !g_relay_txes || (pfrom.m_tx_relay == nullptr); + bool fBlocksOnly = m_ignore_incoming_txs || (pfrom.m_tx_relay == nullptr); // Allow peers with relay permission to send data other than blocks in blocks only mode if (pfrom.HasPermission(PF_RELAY)) { @@ -2759,7 +2974,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat a_recent_block = most_recent_block; } BlockValidationState state; - if (!ActivateBestChain(state, m_chainparams, a_recent_block)) { + if (!::ChainstateActive().ActivateBestChain(state, m_chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } @@ -2767,7 +2982,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat LOCK(cs_main); // Find the last block the caller has in the main chain - const CBlockIndex* pindex = FindForkInGlobalIndex(::ChainActive(), locator); + const CBlockIndex* pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); // Send the rest of the chain if (pindex) @@ -2789,13 +3004,12 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } - WITH_LOCK(pfrom.cs_inventory, pfrom.vInventoryBlockToSend.push_back(pindex->GetBlockHash())); - if (--nLimit <= 0) - { + WITH_LOCK(peer->m_block_inv_mutex, peer->m_blocks_for_inv_relay.push_back(pindex->GetBlockHash())); + if (--nLimit <= 0) { // When this block is requested, we'll send an inv that'll // trigger the peer to getblocks the next batch of inventory. LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); - pfrom.hashContinue = pindex->GetBlockHash(); + WITH_LOCK(peer->m_block_inv_mutex, {peer->m_continuation_block = pindex->GetBlockHash();}); break; } } @@ -2821,7 +3035,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat { LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(req.blockhash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(req.blockhash); if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) { LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId()); return; @@ -2875,7 +3089,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (locator.IsNull()) { // If locator is null, return the hashStop block - pindex = LookupBlockIndex(hashStop); + pindex = g_chainman.m_blockman.LookupBlockIndex(hashStop); if (!pindex) { return; } @@ -2888,7 +3102,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat else { // Find the last block the caller has in the main chain - pindex = FindForkInGlobalIndex(::ChainActive(), locator); + pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); if (pindex) pindex = ::ChainActive().Next(pindex); } @@ -2924,7 +3138,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat // Stop processing the transaction early if // 1) We are in blocks only mode and peer has no relay permission // 2) This peer is a block-relay-only peer - if ((!g_relay_txes && !pfrom.HasPermission(PF_RELAY)) || (pfrom.m_tx_relay == nullptr)) + if ((m_ignore_incoming_txs && !pfrom.HasPermission(PF_RELAY)) || (pfrom.m_tx_relay == nullptr)) { LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId()); pfrom.fDisconnect = true; @@ -3152,14 +3366,14 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat { LOCK(cs_main); - if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { + if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!::ChainstateActive().IsInitialBlockDownload()) m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); return; } - if (!LookupBlockIndex(cmpctblock.header.GetHash())) { + if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) { received_new_header = true; } } @@ -3319,7 +3533,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat // 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, {cmpctblock.header}, /*via_compact_block=*/true); + return ProcessHeadersMessage(pfrom, *peer, {cmpctblock.header}, /*via_compact_block=*/true); } if (fBlockReconstructed) { @@ -3462,7 +3676,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - return ProcessHeadersMessage(pfrom, headers, /*via_compact_block=*/false); + return ProcessHeadersMessage(pfrom, *peer, headers, /*via_compact_block=*/false); } if (msg_type == NetMsgType::BLOCK) @@ -3640,6 +3854,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (msg_type == NetMsgType::FILTERLOAD) { if (!(pfrom.GetLocalServices() & NODE_BLOOM)) { + LogPrint(BCLog::NET, "filterload received despite not offering bloom services from peer=%d; disconnecting\n", pfrom.GetId()); pfrom.fDisconnect = true; return; } @@ -3662,6 +3877,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (msg_type == NetMsgType::FILTERADD) { if (!(pfrom.GetLocalServices() & NODE_BLOOM)) { + LogPrint(BCLog::NET, "filteradd received despite not offering bloom services from peer=%d; disconnecting\n", pfrom.GetId()); pfrom.fDisconnect = true; return; } @@ -3689,6 +3905,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (msg_type == NetMsgType::FILTERCLEAR) { if (!(pfrom.GetLocalServices() & NODE_BLOOM)) { + LogPrint(BCLog::NET, "filterclear received despite not offering bloom services from peer=%d; disconnecting\n", pfrom.GetId()); pfrom.fDisconnect = true; return; } @@ -3706,7 +3923,6 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat vRecv >> newFeeFilter; if (MoneyRange(newFeeFilter)) { if (pfrom.m_tx_relay != nullptr) { - LOCK(pfrom.m_tx_relay->cs_feeFilter); pfrom.m_tx_relay->minFeeFilter = newFeeFilter; } LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom.GetId()); @@ -3750,7 +3966,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat return; } -bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode) +bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode) { const NodeId peer_id{pnode.GetId()}; PeerRef peer = GetPeerRef(peer_id); @@ -3786,13 +4002,13 @@ bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode) } // Normal case: Disconnect the peer and discourage all nodes sharing the address - LogPrintf("Disconnecting and discouraging peer %d!\n", peer_id); + LogPrint(BCLog::NET, "Disconnecting and discouraging peer %d!\n", peer_id); if (m_banman) m_banman->Discourage(pnode.addr); m_connman.DisconnectNode(pnode.addr); return true; } -bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) +bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) { bool fMoreWork = false; @@ -3867,7 +4083,7 @@ bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgP return fMoreWork; } -void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) +void PeerManagerImpl::ConsiderEviction(CNode& pto, int64_t time_in_seconds) { AssertLockHeld(cs_main); @@ -3920,13 +4136,56 @@ void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) } } -void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) +void PeerManagerImpl::EvictExtraOutboundPeers(int64_t time_in_seconds) { - // Check whether we have too many outbound peers - 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 + // If we have any extra block-relay-only peers, disconnect the youngest unless + // it's given us a block -- in which case, compare with the second-youngest, and + // out of those two, disconnect the peer who least recently gave us a block. + // The youngest block-relay-only peer would be the extra peer we connected + // to temporarily in order to sync our tip; see net.cpp. + // Note that we use higher nodeid as a measure for most recent connection. + if (m_connman.GetExtraBlockRelayCount() > 0) { + std::pair<NodeId, int64_t> youngest_peer{-1, 0}, next_youngest_peer{-1, 0}; + + m_connman.ForEachNode([&](CNode* pnode) { + if (!pnode->IsBlockOnlyConn() || pnode->fDisconnect) return; + if (pnode->GetId() > youngest_peer.first) { + next_youngest_peer = youngest_peer; + youngest_peer.first = pnode->GetId(); + youngest_peer.second = pnode->nLastBlockTime; + } + }); + NodeId to_disconnect = youngest_peer.first; + if (youngest_peer.second > next_youngest_peer.second) { + // Our newest block-relay-only peer gave us a block more recently; + // disconnect our second youngest. + to_disconnect = next_youngest_peer.first; + } + m_connman.ForNode(to_disconnect, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + AssertLockHeld(::cs_main); + // Make sure we're not getting a block right now, and that + // we've been connected long enough for this eviction to happen + // at all. + // Note that we only request blocks from a peer if we learn of a + // valid headers chain with at least as much work as our tip. + CNodeState *node_state = State(pnode->GetId()); + if (node_state == nullptr || + (time_in_seconds - pnode->nTimeConnected >= MINIMUM_CONNECT_TIME && node_state->nBlocksInFlight == 0)) { + pnode->fDisconnect = true; + LogPrint(BCLog::NET, "disconnecting extra block-relay-only peer=%d (last block received at time %d)\n", pnode->GetId(), pnode->nLastBlockTime); + return true; + } else { + LogPrint(BCLog::NET, "keeping block-relay-only peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n", + pnode->GetId(), pnode->nTimeConnected, node_state->nBlocksInFlight); + } + return false; + }); + } + + // Check whether we have too many outbound-full-relay peers + if (m_connman.GetExtraFullOutboundCount() > 0) { + // If we have more outbound-full-relay peers than we target, disconnect one. + // Pick the outbound-full-relay peer that least recently announced // us a new block, with ties broken by choosing the more recent // connection (higher node id) NodeId worst_peer = -1; @@ -3935,14 +4194,13 @@ void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); - // Ignore non-outbound peers, or nodes marked for disconnect already - if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return; + // Only consider outbound-full-relay peers that are not already + // marked for disconnection + if (!pnode->IsFullOutboundConn() || 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 if (state->m_chain_sync.m_protect) return; - // Don't evict our block-relay-only peers. - if (pnode->m_tx_relay == nullptr) return; if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) { worst_peer = pnode->GetId(); oldest_block_announcement = state->m_last_block_announcement; @@ -3979,7 +4237,7 @@ void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) } } -void PeerManager::CheckForStaleTipAndEvictPeers() +void PeerManagerImpl::CheckForStaleTipAndEvictPeers() { LOCK(cs_main); @@ -3998,6 +4256,11 @@ void PeerManager::CheckForStaleTipAndEvictPeers() } m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; } + + if (!m_initial_sync_finished && CanDirectFetch(m_chainparams.GetConsensus())) { + m_connman.StartExtraBlockRelayPeers(); + m_initial_sync_finished = true; + } } namespace { @@ -4021,8 +4284,9 @@ public: }; } -bool PeerManager::SendMessages(CNode* pto) +bool PeerManagerImpl::SendMessages(CNode* pto) { + PeerRef peer = GetPeerRef(pto->GetId()); const Consensus::Params& consensusParams = m_chainparams.GetConsensus(); // We must call MaybeDiscourageAndDisconnect first, to ensure that we'll @@ -4074,6 +4338,15 @@ bool PeerManager::SendMessages(CNode* pto) auto current_time = GetTime<std::chrono::microseconds>(); if (pto->RelayAddrsWithConn() && !::ChainstateActive().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) { + // If we've sent before, clear the bloom filter for the peer, so that our + // self-announcement will actually go out. + // This might be unnecessary if the bloom filter has already rolled + // over since our last self-announcement, but there is only a small + // bandwidth cost that we can incur by doing this (which happens + // once a day on average). + if (pto->m_next_local_addr_send != 0us) { + pto->m_addr_known->reset(); + } AdvertiseLocal(pto); pto->m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL); } @@ -4139,7 +4412,7 @@ bool PeerManager::SendMessages(CNode* pto) got back an empty response. */ if (pindexStart->pprev) pindexStart = pindexStart->pprev; - LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight); + LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height); m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexStart), uint256())); } } @@ -4155,11 +4428,11 @@ bool PeerManager::SendMessages(CNode* pto) // If no header would connect, or if we have too many // blocks, or if the peer doesn't want headers, just // add all to the inv queue. - LOCK(pto->cs_inventory); + LOCK(peer->m_block_inv_mutex); std::vector<CBlock> vHeaders; bool fRevertToInv = ((!state.fPreferHeaders && - (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) || - pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); + (!state.fPreferHeaderAndIDs || peer->m_blocks_for_headers_relay.size() > 1)) || + peer->m_blocks_for_headers_relay.size() > MAX_BLOCKS_TO_ANNOUNCE); const CBlockIndex *pBestIndex = nullptr; // last header queued for delivery ProcessBlockAvailability(pto->GetId()); // ensure pindexBestKnownBlock is up-to-date @@ -4168,8 +4441,8 @@ bool PeerManager::SendMessages(CNode* pto) // Try to find first header that our peer doesn't have, and // then send all headers past that one. If we come across any // headers that aren't on ::ChainActive(), give up. - for (const uint256 &hash : pto->vBlockHashesToAnnounce) { - const CBlockIndex* pindex = LookupBlockIndex(hash); + for (const uint256& hash : peer->m_blocks_for_headers_relay) { + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); assert(pindex); if (::ChainActive()[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block @@ -4185,7 +4458,7 @@ bool PeerManager::SendMessages(CNode* pto) // which should be caught by the prior check), but one // way this could happen is by using invalidateblock / // reconsiderblock repeatedly on the tip, causing it to - // be added multiple times to vBlockHashesToAnnounce. + // be added multiple times to m_blocks_for_headers_relay. // Robustly deal with this rare situation by reverting // to an inv. fRevertToInv = true; @@ -4257,11 +4530,11 @@ bool PeerManager::SendMessages(CNode* pto) } if (fRevertToInv) { // If falling back to using an inv, just try to inv the tip. - // The last entry in vBlockHashesToAnnounce was our tip at some point + // The last entry in m_blocks_for_headers_relay was our tip at some point // in the past. - if (!pto->vBlockHashesToAnnounce.empty()) { - const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); - const CBlockIndex* pindex = LookupBlockIndex(hashToAnnounce); + if (!peer->m_blocks_for_headers_relay.empty()) { + const uint256& hashToAnnounce = peer->m_blocks_for_headers_relay.back(); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashToAnnounce); assert(pindex); // Warn if we're announcing a block that is not on the main chain. @@ -4274,13 +4547,13 @@ bool PeerManager::SendMessages(CNode* pto) // If the peer's chain has this block, don't inv it back. if (!PeerHasHeader(&state, pindex)) { - pto->vInventoryBlockToSend.push_back(hashToAnnounce); + peer->m_blocks_for_inv_relay.push_back(hashToAnnounce); LogPrint(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__, pto->GetId(), hashToAnnounce.ToString()); } } } - pto->vBlockHashesToAnnounce.clear(); + peer->m_blocks_for_headers_relay.clear(); } // @@ -4288,18 +4561,18 @@ bool PeerManager::SendMessages(CNode* pto) // std::vector<CInv> vInv; { - LOCK(pto->cs_inventory); - vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); + LOCK(peer->m_block_inv_mutex); + vInv.reserve(std::max<size_t>(peer->m_blocks_for_inv_relay.size(), INVENTORY_BROADCAST_MAX)); // Add blocks - for (const uint256& hash : pto->vInventoryBlockToSend) { + for (const uint256& hash : peer->m_blocks_for_inv_relay) { vInv.push_back(CInv(MSG_BLOCK, hash)); if (vInv.size() == MAX_INV_SZ) { m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } - pto->vInventoryBlockToSend.clear(); + peer->m_blocks_for_inv_relay.clear(); if (pto->m_tx_relay != nullptr) { LOCK(pto->m_tx_relay->cs_tx_inventory); @@ -4325,11 +4598,7 @@ bool PeerManager::SendMessages(CNode* pto) if (fSendTrickle && pto->m_tx_relay->fSendMempool) { auto vtxinfo = m_mempool.infoAll(); pto->m_tx_relay->fSendMempool = false; - CFeeRate filterrate; - { - LOCK(pto->m_tx_relay->cs_feeFilter); - filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter); - } + const CFeeRate filterrate{pto->m_tx_relay->minFeeFilter.load()}; LOCK(pto->m_tx_relay->cs_filter); @@ -4363,11 +4632,7 @@ bool PeerManager::SendMessages(CNode* pto) for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) { vInvTx.push_back(it); } - CFeeRate filterrate; - { - LOCK(pto->m_tx_relay->cs_feeFilter); - filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter); - } + const CFeeRate filterrate{pto->m_tx_relay->minFeeFilter.load()}; // Topologically and fee-rate sort the inventory we send for privacy and priority reasons. // A heap is used so that not all items need sorting if only a few are being sent. CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool, state.m_wtxid_relay); @@ -4528,7 +4793,7 @@ bool PeerManager::SendMessages(CNode* pto) } // - // Message: getdata (non-blocks) + // Message: getdata (transactions) // std::vector<std::pair<NodeId, GenTxid>> expired; auto requestable = m_txrequest.GetRequestable(pto->GetId(), current_time, &expired); diff --git a/src/net_processing.h b/src/net_processing.h index 87eee566de..eaa3b142a8 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -6,19 +6,13 @@ #ifndef BITCOIN_NET_PROCESSING_H #define BITCOIN_NET_PROCESSING_H -#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; @@ -32,129 +26,48 @@ 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 PeerManager final : public CValidationInterface, public NetEventsInterface { -public: - PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, - CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool); +struct CNodeStateStats { + int nSyncHeight = -1; + int nCommonHeight = -1; + int m_starting_height = -1; + std::vector<int> vHeightInFlight; +}; - /** - * Overridden from CValidationInterface. - */ - void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override; - void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override; - /** - * Overridden from CValidationInterface. - */ - void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; - /** - * Overridden from CValidationInterface. - */ - void BlockChecked(const CBlock& block, const BlockValidationState& state) override; - /** - * Overridden from CValidationInterface. - */ - void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; +class PeerManager : public CValidationInterface, public NetEventsInterface +{ +public: + static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs); + virtual ~PeerManager() { } - /** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */ - void InitializeNode(CNode* pnode) override; - /** Handle removal of a peer by updating various state and removing it from mapNodeState */ - void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override; - /** - * Process protocol messages received from a given node - * - * @param[in] pfrom The node which we have received messages from. - * @param[in] interrupt Interrupt condition for processing threads - */ - bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; - /** - * Send queued protocol messages to be sent to a give node. - * - * @param[in] pto The node which we are sending messages to. - * @return True if there is more work to be done - */ - bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); + /** Get statistics from node state */ + virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) = 0; - /** 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(); - /** 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; + /** Whether this node ignores txs received over p2p. */ + virtual bool IgnoresIncomingTxs() = 0; - /** 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); + /** Set the best height */ + virtual void SetBestHeight(int height) = 0; /** * 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); + virtual void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) = 0; -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 + * Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound. + * Public for unit testing. */ - 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); + virtual void CheckForStaleTipAndEvictPeers() = 0; - 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 m_misbehavior_score = 0; - int nSyncHeight = -1; - int nCommonHeight = -1; - std::vector<int> vHeightInFlight; + /** Process a single message from a peer. Public for fuzz testing */ + virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, + const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0; }; -/** Get statistics from node state */ -bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); - /** Relay transaction to every node */ void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 35e9161f58..85e46fd373 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -437,6 +437,11 @@ bool CNetAddr::IsValid() const return false; } + // CJDNS addresses always start with 0xfc + if (IsCJDNS() && (m_addr[0] != 0xFC)) { + return false; + } + // documentation IPv6 address if (IsRFC3849()) return false; @@ -1063,15 +1068,24 @@ CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet() CSubNet::CSubNet(const CNetAddr& addr) : CSubNet() { - valid = addr.IsIPv4() || addr.IsIPv6(); - if (!valid) { + switch (addr.m_net) { + case NET_IPV4: + case NET_IPV6: + valid = true; + assert(addr.m_addr.size() <= sizeof(netmask)); + memset(netmask, 0xFF, addr.m_addr.size()); + break; + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + valid = true; + break; + case NET_INTERNAL: + case NET_UNROUTABLE: + case NET_MAX: return; } - assert(addr.m_addr.size() <= sizeof(netmask)); - - memset(netmask, 0xFF, addr.m_addr.size()); - network = addr; } @@ -1083,6 +1097,21 @@ bool CSubNet::Match(const CNetAddr &addr) const { if (!valid || !addr.IsValid() || network.m_net != addr.m_net) return false; + + switch (network.m_net) { + case NET_IPV4: + case NET_IPV6: + break; + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + case NET_INTERNAL: + return addr == network; + case NET_UNROUTABLE: + case NET_MAX: + return false; + } + 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]) { @@ -1094,18 +1123,35 @@ bool CSubNet::Match(const CNetAddr &addr) const std::string CSubNet::ToString() const { - assert(network.m_addr.size() <= sizeof(netmask)); + std::string suffix; - uint8_t cidr = 0; + switch (network.m_net) { + case NET_IPV4: + case NET_IPV6: { + assert(network.m_addr.size() <= sizeof(netmask)); - for (size_t i = 0; i < network.m_addr.size(); ++i) { - if (netmask[i] == 0x00) { - break; + uint8_t cidr = 0; + + for (size_t i = 0; i < network.m_addr.size(); ++i) { + if (netmask[i] == 0x00) { + break; + } + cidr += NetmaskBits(netmask[i]); } - cidr += NetmaskBits(netmask[i]); + + suffix = strprintf("/%u", cidr); + break; + } + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + case NET_INTERNAL: + case NET_UNROUTABLE: + case NET_MAX: + break; } - return network.ToString() + strprintf("/%u", cidr); + return network.ToString() + suffix; } bool CSubNet::IsValid() const @@ -1115,7 +1161,19 @@ bool CSubNet::IsValid() const bool CSubNet::SanityCheck() const { - if (!(network.IsIPv4() || network.IsIPv6())) return false; + switch (network.m_net) { + case NET_IPV4: + case NET_IPV6: + break; + case NET_ONION: + case NET_I2P: + case NET_CJDNS: + return true; + case NET_INTERNAL: + case NET_UNROUTABLE: + case NET_MAX: + return false; + } for (size_t x = 0; x < network.m_addr.size(); ++x) { if (network.m_addr[x] & ~netmask[x]) return false; diff --git a/src/netaddress.h b/src/netaddress.h index 29b2eaafeb..b9eade7fd5 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -462,11 +462,33 @@ class CSubNet bool SanityCheck() const; public: + /** + * Construct an invalid subnet (empty, `Match()` always returns false). + */ CSubNet(); + + /** + * Construct from a given network start and number of bits (CIDR mask). + * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is + * created. + * @param[in] mask CIDR mask, must be in [0, 32] for IPv4 addresses and in [0, 128] for + * IPv6 addresses. Otherwise an invalid subnet is created. + */ CSubNet(const CNetAddr& addr, uint8_t mask); + + /** + * Construct from a given network start and mask. + * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is + * created. + * @param[in] mask Network mask, must be of the same type as `addr` and not contain 0-bits + * followed by 1-bits. Otherwise an invalid subnet is created. + */ CSubNet(const CNetAddr& addr, const CNetAddr& mask); - //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128) + /** + * Construct a single-host subnet. + * @param[in] addr The sole address to be contained in the subnet, can also be non-IPv[46]. + */ explicit CSubNet(const CNetAddr& addr); bool Match(const CNetAddr &addr) const; @@ -483,7 +505,7 @@ class CSubNet 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 + // to store the relevant bytes for an IPv4 mask. For compatibility reasons, keep doing so in // serialized form. unsigned char dummy[12] = {0}; READWRITE(dummy); diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h index ffb3fe2f29..89fb4758f9 100644 --- a/src/netmessagemaker.h +++ b/src/netmessagemaker.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp index fb46ea1731..56113cb731 100644 --- a/src/node/coinstats.cpp +++ b/src/node/coinstats.cpp @@ -63,7 +63,7 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const stats.hashBlock = pcursor->GetBestBlock(); { LOCK(cs_main); - stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; + stats.nHeight = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock)->nHeight; } PrepareHash(hash_obj, stats); @@ -112,7 +112,7 @@ bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_t } // The legacy hash serializes the hashBlock -static void PrepareHash(CHashWriter& ss, CCoinsStats& stats) +static void PrepareHash(CHashWriter& ss, const CCoinsStats& stats) { ss << stats.hashBlock; } diff --git a/src/node/coinstats.h b/src/node/coinstats.h index 2a7441c10e..7c56bfc2ad 100644 --- a/src/node/coinstats.h +++ b/src/node/coinstats.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/node/context.cpp b/src/node/context.cpp index 49d0c37235..958221a913 100644 --- a/src/node/context.cpp +++ b/src/node/context.cpp @@ -8,6 +8,7 @@ #include <interfaces/chain.h> #include <net.h> #include <net_processing.h> +#include <policy/fees.h> #include <scheduler.h> #include <txmempool.h> diff --git a/src/node/context.h b/src/node/context.h index 3228831ed1..9b611bf8f5 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -12,6 +12,7 @@ class ArgsManager; class BanMan; +class CBlockPolicyEstimator; class CConnman; class CScheduler; class CTxMemPool; @@ -36,6 +37,7 @@ class WalletClient; struct NodeContext { std::unique_ptr<CConnman> connman; std::unique_ptr<CTxMemPool> mempool; + std::unique_ptr<CBlockPolicyEstimator> fee_estimator; 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; diff --git a/src/interfaces/chain.cpp b/src/node/interfaces.cpp index 4c5ebe66fc..b7efd68cfd 100644 --- a/src/interfaces/chain.cpp +++ b/src/node/interfaces.cpp @@ -2,18 +2,26 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <interfaces/chain.h> - +#include <addrdb.h> +#include <banman.h> +#include <boost/signals2/signal.hpp> #include <chain.h> #include <chainparams.h> +#include <init.h> +#include <interfaces/chain.h> #include <interfaces/handler.h> +#include <interfaces/node.h> #include <interfaces/wallet.h> +#include <mapport.h> #include <net.h> #include <net_processing.h> +#include <netaddress.h> +#include <netbase.h> #include <node/coin.h> #include <node/context.h> #include <node/transaction.h> #include <node/ui_interface.h> +#include <policy/feerate.h> #include <policy/fees.h> #include <policy/policy.h> #include <policy/rbf.h> @@ -23,22 +31,274 @@ #include <rpc/protocol.h> #include <rpc/server.h> #include <shutdown.h> +#include <support/allocators/secure.h> #include <sync.h> #include <timedata.h> #include <txmempool.h> #include <uint256.h> #include <univalue.h> +#include <util/check.h> +#include <util/ref.h> #include <util/system.h> +#include <util/translation.h> #include <validation.h> #include <validationinterface.h> +#include <warnings.h> + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif #include <memory> #include <utility> -namespace interfaces { +using interfaces::BlockTip; +using interfaces::Chain; +using interfaces::FoundBlock; +using interfaces::Handler; +using interfaces::MakeHandler; +using interfaces::Node; +using interfaces::WalletClient; + +namespace node { namespace { +class NodeImpl : public Node +{ +public: + explicit NodeImpl(NodeContext* context) { setContext(context); } + 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(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() && + AppInitLockDataDirectory() && AppInitInterfaces(*m_context); + } + bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override + { + return AppInitMain(m_context_ref, *m_context, tip_info); + } + void appShutdown() override + { + Interrupt(*m_context); + Shutdown(*m_context); + } + void startShutdown() override + { + StartShutdown(); + // Stop RPC for clean shutdown if any of waitfor* commands is executed. + if (gArgs.GetBoolArg("-server", false)) { + InterruptRPC(); + StopRPC(); + } + } + bool shutdownRequested() override { return ShutdownRequested(); } + void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } + bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } + size_t getNodeCount(CConnman::NumConnections flags) override + { + return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; + } + bool getNodesStats(NodesStats& stats) override + { + stats.clear(); + + if (m_context->connman) { + std::vector<CNodeStats> stats_temp; + m_context->connman->GetNodeStats(stats_temp); + + stats.reserve(stats_temp.size()); + for (auto& node_stats_temp : stats_temp) { + stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); + } + + // Try to retrieve the CNodeStateStats for each node. + if (m_context->peerman) { + TRY_LOCK(::cs_main, lockMain); + if (lockMain) { + for (auto& node_stats : stats) { + std::get<1>(node_stats) = + m_context->peerman->GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); + } + } + } + return true; + } + return false; + } + bool getBanned(banmap_t& banmap) override + { + if (m_context->banman) { + m_context->banman->GetBanned(banmap); + return true; + } + return false; + } + bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override + { + if (m_context->banman) { + m_context->banman->Ban(net_addr, ban_time_offset); + return true; + } + return false; + } + bool unban(const CSubNet& ip) override + { + if (m_context->banman) { + m_context->banman->Unban(ip); + return true; + } + return false; + } + bool disconnectByAddress(const CNetAddr& net_addr) override + { + if (m_context->connman) { + return m_context->connman->DisconnectNode(net_addr); + } + return false; + } + bool disconnectById(NodeId id) override + { + if (m_context->connman) { + return m_context->connman->DisconnectNode(id); + } + return false; + } + int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; } + int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } + size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } + size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } + bool getHeaderTip(int& height, int64_t& block_time) override + { + LOCK(::cs_main); + if (::pindexBestHeader) { + height = ::pindexBestHeader->nHeight; + block_time = ::pindexBestHeader->GetBlockTime(); + return true; + } + return false; + } + int getNumBlocks() override + { + LOCK(::cs_main); + return ::ChainActive().Height(); + } + uint256 getBestBlockHash() override + { + const CBlockIndex* tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip()); + return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash(); + } + int64_t getLastBlockTime() override + { + LOCK(::cs_main); + if (::ChainActive().Tip()) { + return ::ChainActive().Tip()->GetBlockTime(); + } + return Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network + } + double getVerificationProgress() override + { + const CBlockIndex* tip; + { + LOCK(::cs_main); + tip = ::ChainActive().Tip(); + } + return GuessVerificationProgress(Params().TxData(), tip); + } + bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } + bool getReindex() override { return ::fReindex; } + bool getImporting() override { return ::fImporting; } + void setNetworkActive(bool active) override + { + if (m_context->connman) { + m_context->connman->SetNetworkActive(active); + } + } + bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); } + CFeeRate getDustRelayFee() override { return ::dustRelayFee; } + UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override + { + JSONRPCRequest req(m_context_ref); + req.params = params; + req.strMethod = command; + req.URI = uri; + return ::tableRPC.execute(req); + } + std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); } + void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } + void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } + bool getUnspentOutput(const COutPoint& output, Coin& coin) override + { + LOCK(::cs_main); + return ::ChainstateActive().CoinsTip().GetCoin(output, coin); + } + WalletClient& walletClient() override + { + return *Assert(m_context->wallet_client); + } + std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override + { + return MakeHandler(::uiInterface.InitMessage_connect(fn)); + } + std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override + { + return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); + } + std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override + { + return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); + } + std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override + { + return MakeHandler(::uiInterface.ShowProgress_connect(fn)); + } + std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override + { + return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn)); + } + std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override + { + return MakeHandler(::uiInterface.NotifyNetworkActiveChanged_connect(fn)); + } + std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override + { + return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)); + } + std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override + { + return MakeHandler(::uiInterface.BannedListChanged_connect(fn)); + } + std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override + { + return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) { + fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, + GuessVerificationProgress(Params().TxData(), block)); + })); + } + std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) override + { + return MakeHandler( + ::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, const CBlockIndex* block) { + fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, + /* verification progress is unused when a header was received */ 0); + })); + } + NodeContext* context() override { return m_context; } + void setContext(NodeContext* context) override + { + m_context = context; + if (context) { + m_context_ref.Set(*context); + } else { + m_context_ref.Clear(); + } + } + NodeContext* m_context{nullptr}; + util::Ref m_context_ref; +}; -bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock) +bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active) { if (!index) return false; if (block.m_hash) *block.m_hash = index->GetBlockHash(); @@ -46,6 +306,8 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<Rec if (block.m_time) *block.m_time = index->GetBlockTime(); if (block.m_max_time) *block.m_max_time = index->GetBlockTimeMax(); if (block.m_mtp_time) *block.m_mtp_time = index->GetMedianTimePast(); + if (block.m_in_active_chain) *block.m_in_active_chain = active[index->nHeight] == index; + if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active); if (block.m_data) { REVERSE_LOCK(lock); if (!ReadBlockFromDisk(*block.m_data, index, Params().GetConsensus())) block.m_data->SetNull(); @@ -148,48 +410,33 @@ public: Optional<int> getHeight() override { LOCK(::cs_main); - int height = ::ChainActive().Height(); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + int height = active.Height(); if (height >= 0) { return height; } return nullopt; } - Optional<int> getBlockHeight(const uint256& hash) override - { - LOCK(::cs_main); - CBlockIndex* block = LookupBlockIndex(hash); - if (block && ::ChainActive().Contains(block)) { - return block->nHeight; - } - return nullopt; - } uint256 getBlockHash(int height) override { LOCK(::cs_main); - CBlockIndex* block = ::ChainActive()[height]; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + CBlockIndex* block = active[height]; assert(block); return block->GetBlockHash(); } bool haveBlockOnDisk(int height) override { LOCK(cs_main); - CBlockIndex* block = ::ChainActive()[height]; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + CBlockIndex* block = active[height]; return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0; } - Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override - { - LOCK(cs_main); - CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height); - if (block) { - if (hash) *hash = block->GetBlockHash(); - return block->nHeight; - } - return nullopt; - } CBlockLocator getTipLocator() override { LOCK(cs_main); - return ::ChainActive().GetLocator(); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return active.GetLocator(); } bool checkFinalTx(const CTransaction& tx) override { @@ -199,7 +446,8 @@ public: Optional<int> findLocatorFork(const CBlockLocator& locator) override { LOCK(cs_main); - if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) { + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(active, locator)) { return fork->nHeight; } return nullopt; @@ -207,53 +455,51 @@ public: bool findBlock(const uint256& hash, const FoundBlock& block) override { WAIT_LOCK(cs_main, lock); - return FillBlock(LookupBlockIndex(hash), block, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return FillBlock(g_chainman.m_blockman.LookupBlockIndex(hash), block, lock, active); } bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override { WAIT_LOCK(cs_main, lock); - return FillBlock(ChainActive().FindEarliestAtLeast(min_time, min_height), block, lock); - } - bool findNextBlock(const uint256& block_hash, int block_height, const FoundBlock& next, bool* reorg) override { - WAIT_LOCK(cs_main, lock); - CBlockIndex* block = ChainActive()[block_height]; - if (block && block->GetBlockHash() != block_hash) block = nullptr; - if (reorg) *reorg = !block; - return FillBlock(block ? ChainActive()[block_height + 1] : nullptr, next, lock); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + return FillBlock(active.FindEarliestAtLeast(min_time, min_height), block, lock, active); } bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out) override { WAIT_LOCK(cs_main, lock); - if (const CBlockIndex* block = LookupBlockIndex(block_hash)) { + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + if (const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) { if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) { - return FillBlock(ancestor, ancestor_out, lock); + return FillBlock(ancestor, ancestor_out, lock, active); } } - return FillBlock(nullptr, ancestor_out, lock); + return FillBlock(nullptr, ancestor_out, lock, active); } bool findAncestorByHash(const uint256& block_hash, const uint256& ancestor_hash, const FoundBlock& ancestor_out) override { WAIT_LOCK(cs_main, lock); - const CBlockIndex* block = LookupBlockIndex(block_hash); - const CBlockIndex* ancestor = LookupBlockIndex(ancestor_hash); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash); + const CBlockIndex* ancestor = g_chainman.m_blockman.LookupBlockIndex(ancestor_hash); if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr; - return FillBlock(ancestor, ancestor_out, lock); + return FillBlock(ancestor, ancestor_out, lock, active); } bool findCommonAncestor(const uint256& block_hash1, const uint256& block_hash2, const FoundBlock& ancestor_out, const FoundBlock& block1_out, const FoundBlock& block2_out) override { WAIT_LOCK(cs_main, lock); - const CBlockIndex* block1 = LookupBlockIndex(block_hash1); - const CBlockIndex* block2 = LookupBlockIndex(block_hash2); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + const CBlockIndex* block1 = g_chainman.m_blockman.LookupBlockIndex(block_hash1); + const CBlockIndex* block2 = g_chainman.m_blockman.LookupBlockIndex(block_hash2); const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr; // Using & instead of && below to avoid short circuiting and leaving // output uninitialized. - return FillBlock(ancestor, ancestor_out, lock) & FillBlock(block1, block1_out, lock) & FillBlock(block2, block2_out, lock); + return FillBlock(ancestor, ancestor_out, lock, active) & FillBlock(block1, block1_out, lock, active) & FillBlock(block2, block2_out, lock, active); } void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(m_node, coins); } double guessVerificationProgress(const uint256& block_hash) override { LOCK(cs_main); - return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash)); + return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash)); } bool hasBlocks(const uint256& block_hash, int min_height, Optional<int> max_height) override { @@ -265,7 +511,7 @@ public: // used to limit the range, and passing min_height that's too low or // max_height that's too high will not crash or change the result. LOCK(::cs_main); - if (CBlockIndex* block = LookupBlockIndex(block_hash)) { + if (CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(block_hash)) { if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height); for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) { // Check pprev to not segfault if min_height is too low @@ -327,11 +573,13 @@ public: } CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override { - return ::feeEstimator.estimateSmartFee(num_blocks, calc, conservative); + if (!m_node.fee_estimator) return {}; + return m_node.fee_estimator->estimateSmartFee(num_blocks, calc, conservative); } unsigned int estimateMaxBlocks() override { - return ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + if (!m_node.fee_estimator) return 0; + return m_node.fee_estimator->HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); } CFeeRate mempoolMinFee() override { @@ -365,7 +613,8 @@ public: { if (!old_tip.IsNull()) { LOCK(::cs_main); - if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); + if (old_tip == active.Tip()->GetBlockHash()) return; } SyncWithValidationInterfaceQueue(); } @@ -411,7 +660,9 @@ public: NodeContext& m_node; }; } // namespace +} // namespace node -std::unique_ptr<Chain> MakeChain(NodeContext& node) { return MakeUnique<ChainImpl>(node); } - +namespace interfaces { +std::unique_ptr<Node> MakeNode(NodeContext* context) { return MakeUnique<node::NodeImpl>(context); } +std::unique_ptr<Chain> MakeChain(NodeContext& context) { return MakeUnique<node::ChainImpl>(context); } } // namespace interfaces diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 97d5aad8e4..d3bb9687a8 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/node/transaction.h b/src/node/transaction.h index 6491700d44..0c016ff04e 100644 --- a/src/node/transaction.h +++ b/src/node/transaction.h @@ -36,6 +36,6 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10}; * @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC. * return error */ -NODISCARD TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback); +[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback); #endif // BITCOIN_NODE_TRANSACTION_H diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h index c8b4d60fd0..fe78cb46bd 100644 --- a/src/node/utxo_snapshot.h +++ b/src/node/utxo_snapshot.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/noui.cpp b/src/noui.cpp index 3c82512fac..6fe5c5638c 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/noui.h b/src/noui.h index 8ec5708328..cf273ffb33 100644 --- a/src/noui.h +++ b/src/noui.h @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2019 The Bitcoin Core developers +// Copyright (c) 2013-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. diff --git a/src/optional.h b/src/optional.h index a382cd7b77..583c56eabd 100644 --- a/src/optional.h +++ b/src/optional.h @@ -1,26 +1,20 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_OPTIONAL_H #define BITCOIN_OPTIONAL_H +#include <optional> #include <utility> -#include <boost/optional.hpp> - //! Substitute for C++17 std::optional +//! DEPRECATED use std::optional in new code. template <typename T> -using Optional = boost::optional<T>; - -//! Substitute for C++17 std::make_optional -template <typename T> -Optional<T> MakeOptional(bool condition, T&& value) -{ - return boost::make_optional(condition, std::forward<T>(value)); -} +using Optional = std::optional<T>; //! Substitute for C++17 std::nullopt -static auto& nullopt = boost::none; +//! DEPRECATED use std::nullopt in new code. +static auto& nullopt = std::nullopt; #endif // BITCOIN_OPTIONAL_H diff --git a/src/outputtype.cpp b/src/outputtype.cpp index e978852826..d96fb282c5 100644 --- a/src/outputtype.cpp +++ b/src/outputtype.cpp @@ -19,8 +19,6 @@ static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy"; static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit"; static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32"; -const std::array<OutputType, 3> OUTPUT_TYPES = {OutputType::LEGACY, OutputType::P2SH_SEGWIT, OutputType::BECH32}; - bool ParseOutputType(const std::string& type, OutputType& output_type) { if (type == OUTPUT_TYPE_STRING_LEGACY) { diff --git a/src/outputtype.h b/src/outputtype.h index 77a16b1d05..88422e5824 100644 --- a/src/outputtype.h +++ b/src/outputtype.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -20,9 +20,13 @@ enum class OutputType { BECH32, }; -extern const std::array<OutputType, 3> OUTPUT_TYPES; +static constexpr auto OUTPUT_TYPES = std::array{ + OutputType::LEGACY, + OutputType::P2SH_SEGWIT, + OutputType::BECH32, +}; -NODISCARD bool ParseOutputType(const std::string& str, OutputType& output_type); +[[nodiscard]] bool ParseOutputType(const std::string& str, OutputType& output_type); const std::string& FormatOutputType(OutputType type); /** diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp index 04e0e117a5..3da85fedf9 100644 --- a/src/policy/feerate.cpp +++ b/src/policy/feerate.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 7c5660ac8a..86ae507957 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 0f31093dbb..7da171d2e1 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -1,28 +1,29 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <policy/fees.h> #include <clientversion.h> +#include <fs.h> +#include <logging.h> #include <streams.h> #include <txmempool.h> #include <util/system.h> -static constexpr double INF_FEERATE = 1e99; - -std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) { - static const std::map<FeeEstimateHorizon, std::string> horizon_strings = { - {FeeEstimateHorizon::SHORT_HALFLIFE, "short"}, - {FeeEstimateHorizon::MED_HALFLIFE, "medium"}, - {FeeEstimateHorizon::LONG_HALFLIFE, "long"}, - }; - auto horizon_string = horizon_strings.find(horizon); +static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat"; - if (horizon_string == horizon_strings.end()) return "unknown"; +static constexpr double INF_FEERATE = 1e99; - return horizon_string->second; +std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) +{ + switch (horizon) { + case FeeEstimateHorizon::SHORT_HALFLIFE: return "short"; + case FeeEstimateHorizon::MED_HALFLIFE: return "medium"; + case FeeEstimateHorizon::LONG_HALFLIFE: return "long"; + } // no default case, so the compiler can warn about missing cases + assert(false); } /** @@ -489,6 +490,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator() { static_assert(MIN_BUCKET_FEERATE > 0, "Min feerate must be nonzero"); size_t bucketIndex = 0; + for (double bucketBoundary = MIN_BUCKET_FEERATE; bucketBoundary <= MAX_BUCKET_FEERATE; bucketBoundary *= FEE_SPACING, bucketIndex++) { buckets.push_back(bucketBoundary); bucketMap[bucketBoundary] = bucketIndex; @@ -500,6 +502,13 @@ CBlockPolicyEstimator::CBlockPolicyEstimator() feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE)); shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE)); longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE)); + + // If the fee estimation file is present, read recorded estimations + fs::path est_filepath = GetDataDir() / FEE_ESTIMATES_FILENAME; + CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION); + if (est_file.IsNull() || !Read(est_file)) { + LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", est_filepath.string()); + } } CBlockPolicyEstimator::~CBlockPolicyEstimator() @@ -632,7 +641,7 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult* result) const { - TxConfirmStats* stats; + TxConfirmStats* stats = nullptr; double sufficientTxs = SUFFICIENT_FEETXS; switch (horizon) { case FeeEstimateHorizon::SHORT_HALFLIFE: { @@ -648,10 +657,8 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr stats = longStats.get(); break; } - default: { - throw std::out_of_range("CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon"); - } - } + } // no default case, so the compiler can warn about missing cases + assert(stats); LOCK(m_cs_fee_estimator); // Return failure if trying to analyze a target we're not tracking @@ -681,10 +688,8 @@ unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon hori case FeeEstimateHorizon::LONG_HALFLIFE: { return longStats->GetMaxConfirms(); } - default: { - throw std::out_of_range("CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon"); - } - } + } // no default case, so the compiler can warn about missing cases + assert(false); } unsigned int CBlockPolicyEstimator::BlockSpan() const @@ -856,6 +861,15 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation return CFeeRate(llround(median)); } +void CBlockPolicyEstimator::Flush() { + FlushUnconfirmed(); + + fs::path est_filepath = GetDataDir() / FEE_ESTIMATES_FILENAME; + CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION); + if (est_file.IsNull() || !Write(est_file)) { + LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", est_filepath.string()); + } +} bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const { @@ -888,8 +902,9 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) LOCK(m_cs_fee_estimator); int nVersionRequired, nVersionThatWrote; filein >> nVersionRequired >> nVersionThatWrote; - if (nVersionRequired > CLIENT_VERSION) - return error("CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired); + if (nVersionRequired > CLIENT_VERSION) { + throw std::runtime_error(strprintf("up-version (%d) fee estimate file", nVersionRequired)); + } // Read fee estimates file into temporary variables so existing data // structures aren't corrupted if there is an exception. @@ -907,8 +922,9 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) std::vector<double> fileBuckets; filein >> fileBuckets; size_t numBuckets = fileBuckets.size(); - if (numBuckets <= 1 || numBuckets > 1000) + if (numBuckets <= 1 || numBuckets > 1000) { throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets"); + } std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE)); std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE)); diff --git a/src/policy/fees.h b/src/policy/fees.h index 8ea8816dc3..c444d71a31 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -11,6 +11,7 @@ #include <random.h> #include <sync.h> +#include <array> #include <map> #include <memory> #include <string> @@ -25,9 +26,15 @@ class TxConfirmStats; /* Identifier for each of the 3 different TxConfirmStats which will track * history over different time horizons. */ enum class FeeEstimateHorizon { - SHORT_HALFLIFE = 0, - MED_HALFLIFE = 1, - LONG_HALFLIFE = 2 + SHORT_HALFLIFE, + MED_HALFLIFE, + LONG_HALFLIFE, +}; + +static constexpr auto ALL_FEE_ESTIMATE_HORIZONS = std::array{ + FeeEstimateHorizon::SHORT_HALFLIFE, + FeeEstimateHorizon::MED_HALFLIFE, + FeeEstimateHorizon::LONG_HALFLIFE, }; std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon); @@ -215,6 +222,9 @@ public: /** Calculation of highest target that estimates are tracked for */ unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; + /** Drop still unconfirmed transactions and record current estimations, if the fee estimation file is present. */ + void Flush(); + private: mutable RecursiveMutex m_cs_fee_estimator; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 91997aa883..2b5fd4179d 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -75,7 +75,7 @@ bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType) bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason) { - if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) { + if (tx.nVersion > TX_MAX_STANDARD_VERSION || tx.nVersion < 1) { reason = "version"; return false; } @@ -92,14 +92,15 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR for (const CTxIn& txin : tx.vin) { - // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed - // keys (remember the 520 byte limit on redeemScript size). That works - // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 - // bytes of scriptSig, which we round off to 1650 bytes for some minor - // future-proofing. That's also enough to spend a 20-of-20 - // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not - // considered standard. - if (txin.scriptSig.size() > 1650) { + // Biggest 'standard' txin involving only keys is a 15-of-15 P2SH + // multisig with compressed keys (remember the 520 byte limit on + // redeemScript size). That works out to a (15*(33+1))+3=513 byte + // redeemScript, 513+1+15*(73+1)+3=1627 bytes of scriptSig, which + // we round off to 1650(MAX_STANDARD_SCRIPTSIG_SIZE) bytes for + // some minor future-proofing. That's also enough to spend a + // 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey + // is not considered standard. + if (txin.scriptSig.size() > MAX_STANDARD_SCRIPTSIG_SIZE) { reason = "scriptsig-size"; return false; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 8090dff4c6..f2a3f35546 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -38,12 +38,14 @@ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; static const bool DEFAULT_PERMIT_BAREMULTISIG = true; /** The maximum number of witness stack items in a standard P2WSH script */ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; -/** The maximum size of each witness stack item in a standard P2WSH script */ +/** The maximum size in bytes 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) */ +/** The maximum size in bytes 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 */ +/** The maximum size in bytes of a standard witnessScript */ static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; +/** The maximum size of a standard ScriptSig */ +static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650; /** Min feerate for defining dust. Historically this has been based on the * minRelayTxFee, however changing the dust limit changes which transactions are * standard and should be done with care and ideally rarely. It makes sense to @@ -88,23 +90,32 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType); - /** - * Check for standard transaction types - * @return True if all outputs (scriptPubKeys) use only standard transaction forms - */ + + +// Changing the default transaction version requires a two step process: first +// adapting relay policy by bumping TX_MAX_STANDARD_VERSION, and then later +// allowing the new transaction version in the wallet/RPC. +static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{2}; + +/** +* Check for standard transaction types +* @return True if all outputs (scriptPubKeys) use only standard transaction forms +*/ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason); - /** - * Check for standard transaction types - * @param[in] mapInputs Map of previous transactions that have outputs we're spending - * @param[in] taproot_active Whether or taproot consensus rules are active (used to decide whether spends of them are permitted) - * @return True if all inputs (scriptSigs) use only standard transaction forms - */ +/** +* Check for standard transaction types +* @param[in] mapInputs Map of previous transactions that have outputs we're spending +* @param[in] taproot_active Whether or taproot consensus rules are active (used to decide whether spends of them are permitted) +* @return True if all inputs (scriptSigs) use only standard transaction forms +*/ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, bool taproot_active); - /** - * Check if the transaction is over standard P2WSH resources limit: - * 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements - * These limits are adequate for multi-signature up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL, - */ +/** +* Check if the transaction is over standard P2WSH resources limit: +* 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements +* These limits are adequate for multisignatures up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL. +* +* Also enforce a maximum stack item size limit and no annexes for tapscript spends. +*/ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); /** Compute the virtual transaction size (weight reinterpreted as bytes). */ diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp index 4b55934891..8125b41c41 100644 --- a/src/policy/rbf.cpp +++ b/src/policy/rbf.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/policy/rbf.h b/src/policy/rbf.h index f84e6e5286..e078070c1c 100644 --- a/src/policy/rbf.h +++ b/src/policy/rbf.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/primitives/block.h b/src/primitives/block.h index fd8fc8b868..2d10853607 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index e6183cf2f4..245206b906 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -77,8 +77,6 @@ uint256 CTransaction::ComputeWitnessHash() const return SerializeHash(*this, SER_GETHASH, 0); } -/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash{}, m_witness_hash{} {} CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 00544f64fe..6bf36ee854 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -262,12 +262,6 @@ public: // Default transaction version. static const int32_t CURRENT_VERSION=2; - // Changing the default transaction version requires a two step process: first - // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date - // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and - // MAX_STANDARD_VERSION will be equal. - static const int32_t MAX_STANDARD_VERSION=2; - // The local variables are made const to prevent unintended modification // without updating the cached hash value. However, CTransaction is not // actually immutable; deserialization and assignment are implemented, @@ -287,12 +281,9 @@ private: uint256 ComputeWitnessHash() const; public: - /** Construct a CTransaction that qualifies as IsNull() */ - CTransaction(); - /** Convert a CMutableTransaction into a CTransaction. */ - explicit CTransaction(const CMutableTransaction &tx); - CTransaction(CMutableTransaction &&tx); + explicit CTransaction(const CMutableTransaction& tx); + CTransaction(CMutableTransaction&& tx); template <typename Stream> inline void Serialize(Stream& s) const { @@ -393,7 +384,6 @@ struct CMutableTransaction }; typedef std::shared_ptr<const CTransaction> CTransactionRef; -static inline CTransactionRef MakeTransactionRef() { return std::make_shared<const CTransaction>(); } template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); } /** A generic txid reference (txid or wtxid). */ diff --git a/src/protocol.cpp b/src/protocol.cpp index dc8f795a0c..56e738eaa8 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -203,7 +203,6 @@ static std::string serviceFlagToStr(size_t bit) switch ((ServiceFlags)service_flag) { case NODE_NONE: abort(); // impossible case NODE_NETWORK: return "NETWORK"; - case NODE_GETUTXO: return "GETUTXO"; case NODE_BLOOM: return "BLOOM"; case NODE_WITNESS: return "WITNESS"; case NODE_COMPACT_FILTERS: return "COMPACT_FILTERS"; diff --git a/src/protocol.h b/src/protocol.h index 309fac621c..f183db0501 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -273,10 +273,6 @@ enum ServiceFlags : uint64_t { // NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently // set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light clients. NODE_NETWORK = (1 << 0), - // NODE_GETUTXO means the node is capable of responding to the getutxo protocol request. - // Bitcoin Core does not support this but a patch set called Bitcoin XT does. - // See BIP 64 for details on how this is implemented. - NODE_GETUTXO = (1 << 1), // NODE_BLOOM means the node is capable and willing to handle bloom-filtered connections. // Bitcoin Core nodes used to support this by default, without advertising this bit, // but no longer do as of protocol version 70011 (= NO_BLOOM_VERSION) diff --git a/src/psbt.cpp b/src/psbt.cpp index f51fab6225..a849b2ea53 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <psbt.h> + +#include <util/check.h> #include <util/strencodings.h> @@ -207,7 +209,8 @@ size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) { void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index) { - const CTxOut& out = psbt.tx->vout.at(index); + CMutableTransaction& tx = *Assert(psbt.tx); + const CTxOut& out = tx.vout.at(index); PSBTOutput& psbt_out = psbt.outputs.at(index); // Fill a SignatureData with output info @@ -217,7 +220,7 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio // Construct a would-be spend of this output, to update sigdata with. // Note that ProduceSignature is used to fill in metadata (not actual signatures), // so provider does not need to provide any private keys (it can be a HidingSigningProvider). - MutableTransactionSignatureCreator creator(psbt.tx.get_ptr(), /* index */ 0, out.nValue, SIGHASH_ALL); + MutableTransactionSignatureCreator creator(&tx, /* index */ 0, out.nValue, SIGHASH_ALL); ProduceSignature(provider, creator, out.scriptPubKey, sigdata); // Put redeem_script, witness_script, key paths, into PSBTOutput. diff --git a/src/psbt.h b/src/psbt.h index 0951b76f83..b566726ee3 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -398,7 +398,7 @@ struct PartiallySignedTransaction /** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the * same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */ - NODISCARD bool Merge(const PartiallySignedTransaction& psbt); + [[nodiscard]] bool Merge(const PartiallySignedTransaction& psbt); bool AddInput(const CTxIn& txin, PSBTInput& psbtin); bool AddOutput(const CTxOut& txout, const PSBTOutput& psbtout); PartiallySignedTransaction() {} @@ -605,11 +605,11 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti * @param[in] psbtxs the PSBTs to combine * @return error (OK if we successfully combined the transactions, other error if they were not compatible) */ -NODISCARD TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs); +[[nodiscard]] TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs); //! Decode a base64ed PSBT into a PartiallySignedTransaction -NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error); +[[nodiscard]] bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error); //! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction -NODISCARD bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error); +[[nodiscard]] bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error); #endif // BITCOIN_PSBT_H diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 4d734fc891..fc1624af25 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/pubkey.h b/src/pubkey.h index 0f784b86e4..12514fc3c9 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -170,6 +170,15 @@ public: /* * Check syntactic correctness. * + * When setting a pubkey (Set()) or deserializing fails (its header bytes + * don't match the length of the data), the size is set to 0. Thus, + * by checking size, one can observe whether Set() or deserialization has + * failed. + * + * This does not check for more than that. In particular, it does not verify + * that the coordinates correspond to a point on the curve (see IsFullyValid() + * for that instead). + * * Note that this is consensus critical as CheckECDSASignature() calls it! */ bool IsValid() const @@ -214,7 +223,7 @@ private: public: /** Construct an x-only pubkey from exactly 32 bytes. */ - XOnlyPubKey(Span<const unsigned char> bytes); + explicit XOnlyPubKey(Span<const unsigned char> bytes); /** Verify a Schnorr signature against this public key. * diff --git a/src/qt/README.md b/src/qt/README.md index 30c68db15b..20c712c98d 100644 --- a/src/qt/README.md +++ b/src/qt/README.md @@ -1,10 +1,12 @@ -This directory contains the BitcoinQT graphical user interface (GUI). It uses the cross-platform framework [Qt](https://www1.qt.io/developers/). +This directory contains the source code for the Bitcoin Core graphical user interface (GUI). It uses the [Qt](https://www1.qt.io/developers/) cross-platform framework. The current precise version for Qt 5 is specified in [qt.mk](/depends/packages/qt.mk). ## Compile and run -See build instructions ([macOS](/doc/build-osx.md), [Windows](/doc/build-windows.md), [Unix](/doc/build-unix.md), etc). +See build instructions: [Unix](/doc/build-unix.md), [macOS](/doc/build-osx.md), [Windows](/doc/build-windows.md), [FreeBSD](/doc/build-freebsd.md), [NetBSD](/doc/build-netbsd.md), [OpenBSD](/doc/build-openbsd.md) + +When following your systems build instructions, make sure to install the `Qt` dependencies. To run: @@ -12,84 +14,111 @@ To run: ./src/qt/bitcoin-qt ``` -## Files and directories - -### forms +## Files and Directories -Contains [Designer UI](https://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#using-qt-creator-as-ide), but can be edited using any text editor. +#### forms/ -### locale +- A directory that contains [Designer UI](https://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. These files specify the characteristics of form elements in XML. Qt UI files can be edited with [Qt Creator](#using-qt-creator-as-ide) or using any text editor. -Contains translations. They are periodically updated. The process is described [here](/doc/translation_process.md). +#### locale/ -### res +- Contains translations. They are periodically updated and an effort is made to support as many languages as possible. The process of contributing translations is described in [doc/translation_process.md](/doc/translation_process.md). -Resources such as the icon. +#### res/ -### test + - Contains graphical resources used to enhance the UI experience. -Tests. +#### test/ -### bitcoingui.(h/cpp) +- Functional tests used to ensure proper functionality of the GUI. Significant changes to the GUI code normally require new or updated tests. -Represents the main window of the Bitcoin UI. +#### bitcoingui.(h/cpp) -### \*model.(h/cpp) +- Represents the main window of the Bitcoin UI. -The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](https://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](https://doc.qt.io/qt-5/qvalidator.html). +#### \*model.(h/cpp) -ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`. +- The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](https://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](https://doc.qt.io/qt-5/qvalidator.html). +- ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`. -### \*page.(h/cpp) +#### \*page.(h/cpp) -A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME.page.ui` with a similar `:NAME`. +- A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME.page.ui` with a similar `:NAME`. -### \*dialog.(h/cpp) +#### \*dialog.(h/cpp) -Various dialogs, e.g. to open a URL. Inherit from [QDialog](https://doc.qt.io/qt-5/qdialog.html). +- Various dialogs, e.g. to open a URL. Inherit from [QDialog](https://doc.qt.io/qt-5/qdialog.html). -### paymentserver.(h/cpp) +#### paymentserver.(h/cpp) -Used to process BIP21 payment URI requests. Also handles URI based application switching (e.g. when following a bitcoin:... link from a browser). +- (Deprecated) Used to process BIP21 payment URI requests. Also handles URI-based application switching (e.g. when following a bitcoin:... link from a browser). -### walletview.(h/cpp) +#### walletview.(h/cpp) -Represents the view to a single wallet. +- Represents the view to a single wallet. -### Other .h/cpp files +#### Other .h/cpp files * UI elements like BitcoinAmountField, which inherit from QWidget. * `bitcoinstrings.cpp`: automatically generated -* `bitcoinunits.(h/cpp)`: BTC / mBTC / etc handling +* `bitcoinunits.(h/cpp)`: BTC / mBTC / etc. handling * `callback.h` -* `guiconstants.h`: UI colors, app name, etc +* `guiconstants.h`: UI colors, app name, etc. * `guiutil.h`: several helper functions * `macdockiconhandler.(h/mm)`: macOS dock icon handler * `macnotificationhandler.(h/mm)`: display notifications in macOS ## Contribute -See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines. Specifically for Qt: +See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines. + +**Note:** Do not change `local/bitcoin_en.ts`. It is updated [automatically](/doc/translation_process.md#writing-code-with-translations). + +## Using Qt Creator as an IDE + +[Qt Creator](https://www.qt.io/product/development-tools) is a powerful tool which packages a UI designer tool (Qt Designer) and a C++ IDE into one application. This is especially useful if you want to change the UI layout. + +#### Download Qt Creator + +On Unix and macOS, Qt Creator can be installed through your package manager. Alternatively, you can download a binary from the [Qt Website](https://www.qt.io/download/). + +**Note:** If installing from a binary grabbed from the Qt Website: During the installation process, uncheck everything except for `Qt Creator`. + +##### macOS + +```sh +brew install qt-creator +``` + +##### Ubuntu & Debian + +```sh +sudo apt-get install qtcreator +``` + +#### Setup Qt Creator -* don't change `local/bitcoin_en.ts`; this happens [automatically](/doc/translation_process.md#writing-code-with-translations) +1. Make sure you've installed all dependencies specified in your systems build instructions +2. Follow the compile instructions for your system, run `./configure` with the `--enable-debug` flag +3. Start Qt Creator. At the start page, do: `New` -> `Import Project` -> `Import Existing Project` +4. Enter `bitcoin-qt` as the Project Name and enter the absolute path to `src/qt` as Location +5. Check over the file selection, you may need to select the `forms` directory (necessary if you intend to edit *.ui files) +6. Confirm the `Summary` page +7. In the `Projects` tab, select `Manage Kits...` -## Using Qt Creator as IDE + **macOS** + - Under `Kits`: select the default "Desktop" kit + - Under `Compilers`: select `"Clang (x86 64bit in /usr/bin)"` + - Under `Debuggers`: select `"LLDB"` as debugger (you might need to set the path to your LLDB installation) -You can use Qt Creator as an IDE. This is especially useful if you want to change -the UI layout. + **Ubuntu & Debian** -Download and install the community edition of [Qt Creator](https://www.qt.io/download/). -Uncheck everything except Qt Creator during the installation process. + Note: Some of these options may already be set -Instructions for macOS: + - Under `Kits`: select the default "Desktop" kit + - Under `Compilers`: select `"GCC (x86 64bit in /usr/bin)"` + - Under `Debuggers`: select `"GDB"` as debugger -1. Make sure you installed everything through Homebrew mentioned in the [macOS build instructions](/doc/build-osx.md) -2. Use `./configure` with the `--enable-debug` flag -3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project -4. Enter "bitcoin-qt" as project name, enter src/qt as location -5. Leave the file selection as it is -6. Confirm the "summary page" -7. In the "Projects" tab select "Manage Kits..." -8. Select the default "Desktop" kit and select "Clang (x86 64bit in /usr/bin)" as compiler -9. Select LLDB as debugger (you might need to set the path to your installation) -10. Start debugging with Qt Creator (you might need to the executable to "bitcoin-qt" under "Run", which is where you can also add command line arguments) +8. While in the `Projects` tab, ensure that you have the `bitcoin-qt` executable specified under `Run` + - If the executable is not specified: click `"Choose..."`, navigate to `src/qt`, and select `bitcoin-qt` +9. You're all set! Start developing, building, and debugging the Bitcoin Core GUI diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index aa4ec04497..ab6168a541 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -56,7 +56,7 @@ protected: }; AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::AddressBookPage), model(nullptr), mode(_mode), diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 3d303a6f68..c6a364ccbd 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 665c8e6053..bb444f22b3 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -11,7 +11,6 @@ #include <wallet/wallet.h> #include <algorithm> -#include <typeinfo> #include <QFont> #include <QDebug> @@ -82,8 +81,9 @@ public: { for (const auto& address : wallet.getAddresses()) { - if (pk_hash_only && address.dest.type() != typeid(PKHash)) + if (pk_hash_only && !std::holds_alternative<PKHash>(address.dest)) { continue; + } AddressTableEntry::Type addressType = translateTransactionType( QString::fromStdString(address.purpose), address.is_mine); cachedAddressTable.append(AddressTableEntry(addressType, @@ -177,13 +177,17 @@ AddressTableModel::~AddressTableModel() int AddressTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return priv->size(); } int AddressTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return columns.length(); } @@ -257,7 +261,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } else if(index.column() == Address) { CTxDestination newAddress = DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false - if(boost::get<CNoDestination>(&newAddress)) + if(std::get_if<CNoDestination>(&newAddress)) { editStatus = INVALID_ADDRESS; return false; diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 73316cadc4..6cc14654ef 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 3b4967421a..5b1330b81b 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -20,7 +20,7 @@ #include <QPushButton> AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureString* passphrase_out) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::AskPassphraseDialog), mode(_mode), model(nullptr), @@ -199,6 +199,8 @@ void AskPassphraseDialog::textChanged() acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; case Unlock: // Old passphrase x1 + acceptable = !ui->passEdit1->text().isEmpty(); + break; case ChangePass: // Old passphrase x1, new passphrase x2 acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index f3ba882277..66031b08d3 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 2739b21a9d..a01a7bc386 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -11,6 +11,7 @@ #include <QDateTime> #include <QList> +#include <QLocale> #include <QModelIndex> #include <QVariant> @@ -97,13 +98,17 @@ BanTableModel::~BanTableModel() int BanTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return priv->size(); } int BanTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return columns.length(); } @@ -122,7 +127,7 @@ QVariant BanTableModel::data(const QModelIndex &index, int role) const case Bantime: QDateTime date = QDateTime::fromMSecsSinceEpoch(0); date = date.addSecs(rec->banEntry.nBanUntil); - return date.toString(Qt::SystemLocaleLongDate); + return QLocale::system().toString(date, QLocale::LongFormat); } } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 4e1b239bc7..d6d5ba6968 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -51,6 +51,7 @@ #include <QThread> #include <QTimer> #include <QTranslator> +#include <QtGlobal> #if defined(QT_STATICPLUGIN) #include <QtPlugin> @@ -77,7 +78,7 @@ static void RegisterMetaTypes() #ifdef ENABLE_WALLET qRegisterMetaType<WalletModel*>(); #endif - // Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) + // Register typedefs (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1) qRegisterMetaType<CAmount>("CAmount"); qRegisterMetaType<size_t>("size_t"); @@ -466,6 +467,13 @@ int GuiMain(int argc, char* argv[]) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif +#if (QT_VERSION <= QT_VERSION_CHECK(5, 9, 8)) && defined(Q_OS_MACOS) + const auto os_name = QSysInfo::prettyProductName(); + if (os_name.startsWith("macOS 11") || os_name.startsWith("macOS 10.16")) { + QApplication::setStyle("fusion"); + } +#endif + BitcoinApplication app; /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index 52c06828a3..ae698c72a6 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2014 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index a953c991bc..2af3502be3 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index d3e61aac29..c60d9a2c90 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 20120285d8..ccc0a6828c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -30,6 +30,7 @@ #include <qt/macdockiconhandler.h> #endif +#include <functional> #include <chain.h> #include <chainparams.h> #include <interfaces/handler.h> @@ -615,10 +616,10 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH OptionsModel* optionsModel = _clientModel->getOptionsModel(); if (optionsModel && trayIcon) { // be aware of the tray icon disable state change reported by the OptionsModel object. - connect(optionsModel, &OptionsModel::hideTrayIconChanged, this, &BitcoinGUI::setTrayIconVisible); + connect(optionsModel, &OptionsModel::showTrayIconChanged, trayIcon, &QSystemTrayIcon::setVisible); // initialize the disable state of the tray icon with the current value in the model. - setTrayIconVisible(optionsModel->getHideTrayIcon()); + trayIcon->setVisible(optionsModel->getShowTrayIcon()); } } else { // Disable possibility to show main window via action @@ -845,7 +846,7 @@ void BitcoinGUI::showDebugWindowActivateConsole() void BitcoinGUI::showHelpMessageClicked() { - helpMessageDialog->show(); + GUIUtil::bringToFront(helpMessageDialog); } #ifdef ENABLE_WALLET @@ -1387,14 +1388,6 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress) } } -void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon) -{ - if (trayIcon) - { - trayIcon->setVisible(!fHideTrayIcon); - } -} - void BitcoinGUI::showModalOverlay() { if (modalOverlay && (progressBar->isVisible() || modalOverlay->isLayerVisible())) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 17458c5777..147f19e68d 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -318,9 +318,6 @@ public Q_SLOTS: /** Show progress dialog e.g. for verifychain */ void showProgress(const QString &title, int nProgress); - /** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */ - void setTrayIconVisible(bool); - void showModalOverlay(); }; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index fd55c547fc..9660ba99f7 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -8,6 +8,8 @@ #include <cassert> +static constexpr auto MAX_DIGITS_BTC = 16; + BitcoinUnits::BitcoinUnits(QObject *parent): QAbstractListModel(parent), unitlist(availableUnits()) @@ -108,7 +110,9 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator qint64 n_abs = (n > 0 ? n : -n); qint64 quotient = n_abs / coin; QString quotient_str = QString::number(quotient); - if (justify) quotient_str = quotient_str.rightJustified(16 - num_decimals, ' '); + if (justify) { + quotient_str = quotient_str.rightJustified(MAX_DIGITS_BTC - num_decimals, ' '); + } // Use SI-style thin space separators as these are locale independent and can't be // confused with the decimal marker. diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index a2f46c339b..56813b2d19 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -19,6 +19,7 @@ #include <validation.h> #include <stdint.h> +#include <functional> #include <QDebug> #include <QThread> diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 7f12cce1d9..7ac4cc040b 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 7c72858501..ca78c96d70 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -42,7 +42,7 @@ bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const { } CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _model, const PlatformStyle *_platformStyle, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::CoinControlDialog), m_coin_control(coin_control), model(_model), @@ -455,7 +455,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * else if(ExtractDestination(out.txout.scriptPubKey, address)) { CPubKey pubkey; - PKHash *pkhash = boost::get<PKHash>(&address); + PKHash* pkhash = std::get_if<PKHash>(&address); if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 3de7fd6d54..6ceb3de61d 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp index 3945159c26..1467801522 100644 --- a/src/qt/createwalletdialog.cpp +++ b/src/qt/createwalletdialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -9,10 +9,12 @@ #include <qt/createwalletdialog.h> #include <qt/forms/ui_createwalletdialog.h> +#include <qt/guiutil.h> + #include <QPushButton> CreateWalletDialog::CreateWalletDialog(QWidget* parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::CreateWalletDialog) { ui->setupUi(this); diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index e0af9a20a8..e51ed9e656 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,7 +13,7 @@ EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::EditAddressDialog), mapper(nullptr), mode(_mode), diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 3be63156fd..f7ad80bb2d 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui index ea713e1abd..881869a46c 100644 --- a/src/qt/forms/createwalletdialog.ui +++ b/src/qt/forms/createwalletdialog.ui @@ -7,140 +7,135 @@ <x>0</x> <y>0</y> <width>364</width> - <height>213</height> + <height>249</height> </rect> </property> <property name="windowTitle"> <string>Create Wallet</string> </property> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="geometry"> - <rect> - <x>10</x> - <y>170</y> - <width>341</width> - <height>32</height> - </rect> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - <widget class="QLineEdit" name="wallet_name_line_edit"> - <property name="geometry"> - <rect> - <x>120</x> - <y>20</y> - <width>231</width> - <height>24</height> - </rect> - </property> - <property name="placeholderText"> - <string>Wallet</string> - </property> - </widget> - <widget class="QLabel" name="label"> - <property name="geometry"> - <rect> - <x>20</x> - <y>20</y> - <width>101</width> - <height>21</height> - </rect> - </property> - <property name="text"> - <string>Wallet Name</string> - </property> - </widget> - <widget class="QCheckBox" name="encrypt_wallet_checkbox"> - <property name="geometry"> - <rect> - <x>20</x> - <y>50</y> - <width>171</width> - <height>22</height> - </rect> - </property> - <property name="toolTip"> - <string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string> - </property> - <property name="text"> - <string>Encrypt Wallet</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - <widget class="QLabel" name="advanced_options_label"> - <property name="geometry"> - <rect> - <x>20</x> - <y>90</y> - <width>130</width> - <height>21</height> - </rect> - </property> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> - </property> - <property name="text"> - <string>Advanced options</string> - </property> - </widget> - <widget class="QCheckBox" name="disable_privkeys_checkbox"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="geometry"> - <rect> - <x>20</x> - <y>115</y> - <width>171</width> - <height>22</height> - </rect> - </property> - <property name="toolTip"> - <string>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</string> - </property> - <property name="text"> - <string>Disable Private Keys</string> - </property> - </widget> - <widget class="QCheckBox" name="blank_wallet_checkbox"> - <property name="geometry"> - <rect> - <x>20</x> - <y>135</y> - <width>220</width> - <height>22</height> - </rect> - </property> - <property name="toolTip"> - <string>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</string> - </property> - <property name="text"> - <string>Make Blank Wallet</string> - </property> - </widget> - <widget class="QCheckBox" name="descriptor_checkbox"> - <property name="geometry"> - <rect> - <x>20</x> - <y>155</y> - <width>171</width> - <height>22</height> - </rect> - </property> - <property name="toolTip"> - <string>Use descriptors for scriptPubKey management</string> - </property> - <property name="text"> - <string>Descriptor Wallet</string> - </property> - </widget> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="wallet_name_label"> + <property name="text"> + <string>Wallet Name</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="wallet_name_line_edit"> + <property name="minimumSize"> + <size> + <width>262</width> + <height>0</height> + </size> + </property> + <property name="placeholderText"> + <string>Wallet</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="encrypt_wallet_checkbox"> + <property name="toolTip"> + <string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string> + </property> + <property name="text"> + <string>Encrypt Wallet</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_1"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>8</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Advanced Options</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_groupbox"> + <item> + <widget class="QCheckBox" name="disable_privkeys_checkbox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</string> + </property> + <property name="text"> + <string>Disable Private Keys</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="blank_wallet_checkbox"> + <property name="toolTip"> + <string>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</string> + </property> + <property name="text"> + <string>Make Blank Wallet</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="descriptor_checkbox"> + <property name="toolTip"> + <string>Use descriptors for scriptPubKey management</string> + </property> + <property name="text"> + <string>Descriptor Wallet</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> </widget> <tabstops> <tabstop>wallet_name_line_edit</tabstop> diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 139c8e161e..3831852185 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1077,14 +1077,17 @@ </widget> </item> <item row="1" column="0"> - <widget class="QLabel" name="label_23"> + <widget class="QLabel" name="peerConnectionTypeLabel"> + <property name="toolTip"> + <string>The type of peer connection: %1</string> + </property> <property name="text"> - <string>Direction</string> + <string>Connection Type</string> </property> </widget> </item> <item row="1" column="1"> - <widget class="QLabel" name="peerDirection"> + <widget class="QLabel" name="peerConnectionType"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -1100,13 +1103,39 @@ </widget> </item> <item row="2" column="0"> + <widget class="QLabel" name="peerNetworkLabel"> + <property name="toolTip"> + <string>The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</string> + </property> + <property name="text"> + <string>Network</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="peerNetwork"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="3" column="0"> <widget class="QLabel" name="label_21"> <property name="text"> <string>Version</string> </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <widget class="QLabel" name="peerVersion"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1122,14 +1151,14 @@ </property> </widget> </item> - <item row="3" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="label_28"> <property name="text"> <string>User Agent</string> </property> </widget> </item> - <item row="3" column="1"> + <item row="4" column="1"> <widget class="QLabel" name="peerSubversion"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1145,14 +1174,14 @@ </property> </widget> </item> - <item row="4" column="0"> + <item row="5" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Services</string> </property> </widget> </item> - <item row="4" column="1"> + <item row="5" column="1"> <widget class="QLabel" name="peerServices"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1168,14 +1197,14 @@ </property> </widget> </item> - <item row="5" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="label_29"> <property name="text"> <string>Starting Block</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <widget class="QLabel" name="peerHeight"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1191,14 +1220,14 @@ </property> </widget> </item> - <item row="6" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_27"> <property name="text"> <string>Synced Headers</string> </property> </widget> </item> - <item row="6" column="1"> + <item row="7" column="1"> <widget class="QLabel" name="peerSyncHeight"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1214,14 +1243,14 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="8" column="0"> <widget class="QLabel" name="label_25"> <property name="text"> <string>Synced Blocks</string> </property> </widget> </item> - <item row="7" column="1"> + <item row="8" column="1"> <widget class="QLabel" name="peerCommonHeight"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1237,14 +1266,14 @@ </property> </widget> </item> - <item row="8" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_22"> <property name="text"> <string>Connection Time</string> </property> </widget> </item> - <item row="8" column="1"> + <item row="9" column="1"> <widget class="QLabel" name="peerConnTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1260,14 +1289,14 @@ </property> </widget> </item> - <item row="9" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="label_15"> <property name="text"> <string>Last Send</string> </property> </widget> </item> - <item row="9" column="1"> + <item row="10" column="1"> <widget class="QLabel" name="peerLastSend"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1283,14 +1312,14 @@ </property> </widget> </item> - <item row="10" column="0"> + <item row="11" column="0"> <widget class="QLabel" name="label_19"> <property name="text"> <string>Last Receive</string> </property> </widget> </item> - <item row="10" column="1"> + <item row="11" column="1"> <widget class="QLabel" name="peerLastRecv"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1306,14 +1335,14 @@ </property> </widget> </item> - <item row="11" column="0"> + <item row="12" column="0"> <widget class="QLabel" name="label_18"> <property name="text"> <string>Sent</string> </property> </widget> </item> - <item row="11" column="1"> + <item row="12" column="1"> <widget class="QLabel" name="peerBytesSent"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1329,14 +1358,14 @@ </property> </widget> </item> - <item row="12" column="0"> + <item row="13" column="0"> <widget class="QLabel" name="label_20"> <property name="text"> <string>Received</string> </property> </widget> </item> - <item row="12" column="1"> + <item row="13" column="1"> <widget class="QLabel" name="peerBytesRecv"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1352,14 +1381,14 @@ </property> </widget> </item> - <item row="13" column="0"> + <item row="14" column="0"> <widget class="QLabel" name="label_26"> <property name="text"> <string>Ping Time</string> </property> </widget> </item> - <item row="13" column="1"> + <item row="14" column="1"> <widget class="QLabel" name="peerPingTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1375,7 +1404,7 @@ </property> </widget> </item> - <item row="14" column="0"> + <item row="15" column="0"> <widget class="QLabel" name="peerPingWaitLabel"> <property name="toolTip"> <string>The duration of a currently outstanding ping.</string> @@ -1385,7 +1414,7 @@ </property> </widget> </item> - <item row="14" column="1"> + <item row="15" column="1"> <widget class="QLabel" name="peerPingWait"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1401,14 +1430,14 @@ </property> </widget> </item> - <item row="15" column="0"> + <item row="16" column="0"> <widget class="QLabel" name="peerMinPingLabel"> <property name="text"> <string>Min Ping</string> </property> </widget> </item> - <item row="15" column="1"> + <item row="16" column="1"> <widget class="QLabel" name="peerMinPing"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1424,14 +1453,14 @@ </property> </widget> </item> - <item row="16" column="0"> + <item row="17" column="0"> <widget class="QLabel" name="label_timeoffset"> <property name="text"> <string>Time Offset</string> </property> </widget> </item> - <item row="16" column="1"> + <item row="17" column="1"> <widget class="QLabel" name="timeoffset"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1447,7 +1476,7 @@ </property> </widget> </item> - <item row="17" column="0"> + <item row="18" column="0"> <widget class="QLabel" name="peerMappedASLabel"> <property name="toolTip"> <string>The mapped Autonomous System used for diversifying peer selection.</string> @@ -1457,7 +1486,7 @@ </property> </widget> </item> - <item row="17" column="1"> + <item row="18" column="1"> <widget class="QLabel" name="peerMappedAS"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1473,7 +1502,7 @@ </property> </widget> </item> - <item row="18" column="0"> + <item row="19" column="0"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 045c2d3539..8181cc47e2 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -260,6 +260,16 @@ </widget> </item> <item> + <widget class="QCheckBox" name="mapPortNatpmp"> + <property name="toolTip"> + <string>Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</string> + </property> + <property name="text"> + <string>Map port using NA&T-PMP</string> + </property> + </widget> + </item> + <item> <widget class="QCheckBox" name="allowIncoming"> <property name="toolTip"> <string>Accept connections from outside.</string> @@ -568,12 +578,15 @@ </attribute> <layout class="QVBoxLayout" name="verticalLayout_Window"> <item> - <widget class="QCheckBox" name="hideTrayIcon"> + <widget class="QCheckBox" name="showTrayIcon"> <property name="toolTip"> - <string>Hide the icon from the system tray.</string> + <string>Show the icon in the system tray.</string> </property> <property name="text"> - <string>&Hide tray icon</string> + <string>&Show tray icon</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 4d3f90c484..ee9d4a113c 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -504,7 +504,7 @@ </layout> </item> <item> - <widget class="QListView" name="listTransactions"> + <widget class="TransactionOverviewWidget" name="listTransactions"> <property name="styleSheet"> <string notr="true">QListView { background: transparent; }</string> </property> @@ -517,9 +517,15 @@ <property name="horizontalScrollBarPolicy"> <enum>Qt::ScrollBarAlwaysOff</enum> </property> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustToContents</enum> + </property> <property name="selectionMode"> <enum>QAbstractItemView::NoSelection</enum> </property> + <property name="uniformItemSizes"> + <bool>true</bool> + </property> </widget> </item> </layout> @@ -544,6 +550,13 @@ </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>TransactionOverviewWidget</class> + <extends>QListView</extends> + <header>qt/transactionoverviewwidget.h</header> + </customwidget> + </customwidgets> <resources/> <connections/> </ui> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 70e76f765b..376acf0963 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -40,11 +40,14 @@ #include <QFontDatabase> #include <QFontMetrics> #include <QGuiApplication> +#include <QJsonObject> #include <QKeyEvent> #include <QLineEdit> #include <QList> +#include <QLocale> #include <QMenu> #include <QMouseEvent> +#include <QPluginLoader> #include <QProgressDialog> #include <QScreen> #include <QSettings> @@ -67,7 +70,7 @@ namespace GUIUtil { QString dateTimeStr(const QDateTime &date) { - return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); + return QLocale::system().toString(date.date(), QLocale::ShortFormat) + QString(" ") + date.toString("hh:mm"); } QString dateTimeStr(qint64 nTime) @@ -654,7 +657,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) #elif defined(Q_OS_LINUX) // Follow the Desktop Application Autostart Spec: -// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html +// https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html fs::path static GetAutostartDir() { @@ -748,6 +751,34 @@ QString boostPathToQString(const fs::path &path) return QString::fromStdString(path.string()); } +QString NetworkToQString(Network net) +{ + switch (net) { + case NET_UNROUTABLE: return QObject::tr("Unroutable"); + case NET_IPV4: return "IPv4"; + case NET_IPV6: return "IPv6"; + case NET_ONION: return "Onion"; + case NET_I2P: return "I2P"; + case NET_CJDNS: return "CJDNS"; + case NET_INTERNAL: return QObject::tr("Internal"); + case NET_MAX: assert(false); + } // no default case, so the compiler can warn about missing cases + assert(false); +} + +QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes) +{ + switch (conn_type) { + case ConnectionType::INBOUND: return relay_txes ? QObject::tr("Inbound Full Relay") : QObject::tr("Inbound Block Relay"); + case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); + case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); + case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); + case ConnectionType::FEELER: return QObject::tr("Outbound Feeler"); + case ConnectionType::ADDR_FETCH: return QObject::tr("Outbound Address Fetch"); + } // no default case, so the compiler can warn about missing cases + assert(false); +} + QString formatDurationStr(int secs) { QStringList strList; @@ -907,6 +938,20 @@ void LogQtInfo() const std::string plugin_link{"dynamic"}; #endif LogPrintf("Qt %s (%s), plugin=%s (%s)\n", qVersion(), qt_link, QGuiApplication::platformName().toStdString(), plugin_link); + const auto static_plugins = QPluginLoader::staticPlugins(); + if (static_plugins.empty()) { + LogPrintf("No static plugins.\n"); + } else { + LogPrintf("Static plugins:\n"); + for (const QStaticPlugin& p : static_plugins) { + QJsonObject meta_data = p.metaData(); + const std::string plugin_class = meta_data.take(QString("className")).toString().toStdString(); + const int plugin_version = meta_data.take(QString("version")).toInt(); + LogPrintf(" %s, version %d\n", plugin_class, plugin_version); + } + } + + LogPrintf("Style: %s / %s\n", QApplication::style()->objectName().toStdString(), QApplication::style()->metaObject()->className()); LogPrintf("System: %s, %s\n", QSysInfo::prettyProductName().toStdString(), QSysInfo::buildAbi().toStdString()); for (const QScreen* s : QGuiApplication::screens()) { LogPrintf("Screen: %s %dx%d, pixel ratio=%.1f\n", s->name().toStdString(), s->size().width(), s->size().height(), s->devicePixelRatio()); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index c976b4b4bb..edfb5b13a2 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -7,16 +7,18 @@ #include <amount.h> #include <fs.h> +#include <net.h> +#include <netaddress.h> #include <QEvent> #include <QHeaderView> #include <QItemDelegate> +#include <QLabel> #include <QMessageBox> #include <QObject> #include <QProgressBar> #include <QString> #include <QTableView> -#include <QLabel> class QValidatedLineEdit; class SendCoinsRecipient; @@ -43,6 +45,9 @@ QT_END_NAMESPACE */ namespace GUIUtil { + // Use this flags to prevent a "What's This" button in the title bar of the dialog on Windows. + constexpr auto dialog_flags = Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; + // Create human-readable string from date QString dateTimeStr(const QDateTime &datetime); QString dateTimeStr(qint64 nTime); @@ -218,22 +223,28 @@ namespace GUIUtil bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); - /* Convert QString to OS specific boost path through UTF-8 */ + /** Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); - /* Convert OS specific boost path to QString through UTF-8 */ + /** Convert OS specific boost path to QString through UTF-8 */ QString boostPathToQString(const fs::path &path); - /* Convert seconds into a QString with days, hours, mins, secs */ + /** Convert enum Network to QString */ + QString NetworkToQString(Network net); + + /** Convert enum ConnectionType to QString */ + QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes); + + /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(int secs); - /* Format CNodeStats.nServices bitmask into a user-readable string */ + /** Format CNodeStats.nServices bitmask into a user-readable string */ QString formatServicesStr(quint64 mask); - /* Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0*/ + /** Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0 */ QString formatPingTime(int64_t ping_usec); - /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ + /** Format a CNodeCombinedStats.nTimeOffset into a user-readable string */ QString formatTimeOffset(int64_t nTimeOffset); QString formatNiceTimeOffset(qint64 secs); @@ -340,6 +351,18 @@ namespace GUIUtil #endif } + /** + * Queue a function to run in an object's event loop. This can be + * replaced by a call to the QMetaObject::invokeMethod functor overload after Qt 5.10, but + * for now use a QObject::connect for compatibility with older Qt versions, based on + * https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style + */ + template <typename Fn> + void ObjectInvoke(QObject* object, Fn&& function, Qt::ConnectionType connection = Qt::QueuedConnection) + { + QObject source; + QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection); + } } // namespace GUIUtil diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 235722d091..aa6b2665fa 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -119,7 +119,7 @@ int GetPruneTargetGB() } // namespace Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_size_gb) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::Intro), thread(nullptr), signalled(false), diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 7b07777641..1d8af5cbf6 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 4b91c19761..b097ef080c 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -17,12 +17,7 @@ #include <stdint.h> #include <QtDBus> #endif -// Include ApplicationServices.h after QtDbus to avoid redefinition of check(). -// This affects at least OSX 10.6. See /usr/include/AssertMacros.h for details. -// Note: This could also be worked around using: -// #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #ifdef Q_OS_MAC -#include <ApplicationServices/ApplicationServices.h> #include <qt/macnotificationhandler.h> #endif @@ -71,7 +66,7 @@ Notificator::~Notificator() #ifdef USE_DBUS -// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html +// Loosely based on https://www.qtcentre.org/archive/index.php/t-25879.html class FreedesktopImage { public: diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 9a3d43c2a6..10bf82d532 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -11,7 +11,7 @@ #include <QUrl> OpenURIDialog::OpenURIDialog(QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::OpenURIDialog) { ui->setupUi(this); diff --git a/src/qt/openuridialog.h b/src/qt/openuridialog.h index 667af2ec75..efe4b86f37 100644 --- a/src/qt/openuridialog.h +++ b/src/qt/openuridialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index ae6aeb7709..7b8d7871ec 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -24,11 +24,12 @@ #include <QIntValidator> #include <QLocale> #include <QMessageBox> +#include <QSettings> #include <QSystemTrayIcon> #include <QTimer> OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::OptionsDialog), model(nullptr), mapper(nullptr) @@ -50,6 +51,13 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : #ifndef USE_UPNP ui->mapPortUpnp->setEnabled(false); #endif +#ifndef USE_NATPMP + ui->mapPortNatpmp->setEnabled(false); +#endif + connect(this, &QDialog::accepted, [this](){ + QSettings settings; + model->node().mapPort(settings.value("fUseUPnP").toBool(), settings.value("fUseNatpmp").toBool()); + }); ui->proxyIp->setEnabled(false); ui->proxyPort->setEnabled(false); @@ -130,8 +138,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : connect(ui->proxyPortTor, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState); if (!QSystemTrayIcon::isSystemTrayAvailable()) { - ui->hideTrayIcon->setChecked(true); - ui->hideTrayIcon->setEnabled(false); + ui->showTrayIcon->setChecked(false); + ui->showTrayIcon->setEnabled(false); ui->minimizeToTray->setChecked(false); ui->minimizeToTray->setEnabled(false); } @@ -214,6 +222,7 @@ void OptionsDialog::setMapper() /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); + mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); @@ -227,7 +236,7 @@ void OptionsDialog::setMapper() /* Window */ #ifndef Q_OS_MAC if (QSystemTrayIcon::isSystemTrayAvailable()) { - mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon); + mapper->addMapping(ui->showTrayIcon, OptionsModel::ShowTrayIcon); mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); } mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); @@ -286,17 +295,14 @@ void OptionsDialog::on_cancelButton_clicked() reject(); } -void OptionsDialog::on_hideTrayIcon_stateChanged(int fState) +void OptionsDialog::on_showTrayIcon_stateChanged(int state) { - if(fState) - { + if (state == Qt::Checked) { + ui->minimizeToTray->setEnabled(true); + } else { ui->minimizeToTray->setChecked(false); ui->minimizeToTray->setEnabled(false); } - else - { - ui->minimizeToTray->setEnabled(true); - } } void OptionsDialog::togglePruneWarning(bool enabled) diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 568c8b6fd0..1cc96035c6 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -57,7 +57,7 @@ private Q_SLOTS: void on_okButton_clicked(); void on_cancelButton_clicked(); - void on_hideTrayIcon_stateChanged(int fState); + void on_showTrayIcon_stateChanged(int state); void togglePruneWarning(bool enabled); void showRestartWarning(bool fPersistent = false); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1603b12a73..1e0391a35c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -13,11 +13,12 @@ #include <qt/guiutil.h> #include <interfaces/node.h> -#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS +#include <mapport.h> #include <net.h> #include <netbase.h> -#include <txdb.h> // for -dbcache defaults +#include <txdb.h> // for -dbcache defaults #include <util/string.h> +#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS #include <QDebug> #include <QSettings> @@ -54,14 +55,15 @@ void OptionsModel::Init(bool resetSettings) // These are Qt-only settings: // Window - if (!settings.contains("fHideTrayIcon")) + if (!settings.contains("fHideTrayIcon")) { settings.setValue("fHideTrayIcon", false); - fHideTrayIcon = settings.value("fHideTrayIcon").toBool(); - Q_EMIT hideTrayIconChanged(fHideTrayIcon); + } + m_show_tray_icon = !settings.value("fHideTrayIcon").toBool(); + Q_EMIT showTrayIconChanged(m_show_tray_icon); if (!settings.contains("fMinimizeToTray")) settings.setValue("fMinimizeToTray", false); - fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon; + fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && m_show_tray_icon; if (!settings.contains("fMinimizeOnClose")) settings.setValue("fMinimizeOnClose", false); @@ -122,6 +124,13 @@ void OptionsModel::Init(bool resetSettings) if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) addOverriddenOption("-upnp"); + if (!settings.contains("fUseNatpmp")) { + settings.setValue("fUseNatpmp", DEFAULT_NATPMP); + } + if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool())) { + addOverriddenOption("-natpmp"); + } + if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) @@ -272,8 +281,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const { case StartAtStartup: return GUIUtil::GetStartOnSystemStartup(); - case HideTrayIcon: - return fHideTrayIcon; + case ShowTrayIcon: + return m_show_tray_icon; case MinimizeToTray: return fMinimizeToTray; case MapPortUPnP: @@ -281,7 +290,13 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fUseUPnP"); #else return false; -#endif +#endif // USE_UPNP + case MapPortNatpmp: +#ifdef USE_NATPMP + return settings.value("fUseNatpmp"); +#else + return false; +#endif // USE_NATPMP case MinimizeOnClose: return fMinimizeOnClose; @@ -342,10 +357,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case StartAtStartup: successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); break; - case HideTrayIcon: - fHideTrayIcon = value.toBool(); - settings.setValue("fHideTrayIcon", fHideTrayIcon); - Q_EMIT hideTrayIconChanged(fHideTrayIcon); + case ShowTrayIcon: + m_show_tray_icon = value.toBool(); + settings.setValue("fHideTrayIcon", !m_show_tray_icon); + Q_EMIT showTrayIconChanged(m_show_tray_icon); break; case MinimizeToTray: fMinimizeToTray = value.toBool(); @@ -353,7 +368,9 @@ 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()); - node().mapPort(value.toBool()); + break; + case MapPortNatpmp: // core option - can be changed on-the-fly + settings.setValue("fUseNatpmp", value.toBool()); break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 3d9e7bbb80..f7171951a1 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -45,9 +45,10 @@ public: enum OptionID { StartAtStartup, // bool - HideTrayIcon, // bool + ShowTrayIcon, // bool MinimizeToTray, // bool MapPortUPnP, // bool + MapPortNatpmp, // bool MinimizeOnClose, // bool ProxyUse, // bool ProxyIP, // QString @@ -78,7 +79,7 @@ public: void setDisplayUnit(const QVariant &value); /* Explicit getters */ - bool getHideTrayIcon() const { return fHideTrayIcon; } + bool getShowTrayIcon() const { return m_show_tray_icon; } bool getMinimizeToTray() const { return fMinimizeToTray; } bool getMinimizeOnClose() const { return fMinimizeOnClose; } int getDisplayUnit() const { return nDisplayUnit; } @@ -100,7 +101,7 @@ public: private: interfaces::Node* m_node = nullptr; /* Qt-only settings */ - bool fHideTrayIcon; + bool m_show_tray_icon; bool fMinimizeToTray; bool fMinimizeOnClose; QString language; @@ -118,7 +119,7 @@ private: Q_SIGNALS: void displayUnitChanged(int unit); void coinControlFeaturesChanged(bool); - void hideTrayIconChanged(bool); + void showTrayIconChanged(bool); }; #endif // BITCOIN_QT_OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 1297eb8b75..bc542a0833 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -12,6 +12,7 @@ #include <qt/optionsmodel.h> #include <qt/platformstyle.h> #include <qt/transactionfilterproxy.h> +#include <qt/transactionoverviewwidget.h> #include <qt/transactiontablemodel.h> #include <qt/walletmodel.h> @@ -21,6 +22,9 @@ #include <QPainter> #include <QStatusTipEvent> +#include <algorithm> +#include <map> + #define DECORATION_SIZE 54 #define NUM_ITEMS 5 @@ -34,7 +38,7 @@ public: QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC), platformStyle(_platformStyle) { - + connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged); } inline void paint(QPainter *painter, const QStyleOptionViewItem &option, @@ -67,13 +71,15 @@ public: painter->setPen(foreground); QRect boundingRect; - painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address, &boundingRect); + painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect); + int address_rect_min_width = boundingRect.width(); if (index.data(TransactionTableModel::WatchonlyRole).toBool()) { QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole)); QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight); iconWatchonly.paint(painter, watchonlyRect); + address_rect_min_width += 5 + watchonlyRect.width(); } if(amount < 0) @@ -94,23 +100,42 @@ public: { amountText = QString("[") + amountText + QString("]"); } - painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText); + + QRect amount_bounding_rect; + painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter, amountText, &amount_bounding_rect); painter->setPen(option.palette.color(QPalette::Text)); - painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date)); + QRect date_bounding_rect; + painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date), &date_bounding_rect); + + const int minimum_width = std::max(address_rect_min_width, amount_bounding_rect.width() + date_bounding_rect.width()); + const auto search = m_minimum_width.find(index.row()); + if (search == m_minimum_width.end() || search->second != minimum_width) { + m_minimum_width[index.row()] = minimum_width; + Q_EMIT width_changed(index); + } painter->restore(); } inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { - return QSize(DECORATION_SIZE, DECORATION_SIZE); + const auto search = m_minimum_width.find(index.row()); + const int minimum_text_width = search == m_minimum_width.end() ? 0 : search->second; + return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE}; } int unit; - const PlatformStyle *platformStyle; +Q_SIGNALS: + //! An intermediate signal for emitting from the `paint() const` member function. + void width_changed(const QModelIndex& index) const; + +private: + const PlatformStyle* platformStyle; + mutable std::map<int, int> m_minimum_width; }; + #include <qt/overviewpage.moc> OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) : @@ -126,7 +151,6 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) // use a SingleColorIcon for the "out of sync warning" icon QIcon icon = platformStyle->SingleColorIcon(":/icons/warning"); - icon.addPixmap(icon.pixmap(QSize(64,64), QIcon::Normal), QIcon::Disabled); // also set the disabled icon because we are using a disabled QPushButton to work around missing HiDPI support of QLabel (https://bugreports.qt.io/browse/QTBUG-42503) ui->labelTransactionsStatus->setIcon(icon); ui->labelWalletStatus->setIcon(icon); @@ -136,7 +160,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); - connect(ui->listTransactions, &QListView::clicked, this, &OverviewPage::handleTransactionClicked); + connect(ui->listTransactions, &TransactionOverviewWidget::clicked, this, &OverviewPage::handleTransactionClicked); // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 4cf673b6a6..578ef601fb 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 86d681aa6f..96f6202874 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index eaf2bafe59..08b83244ab 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 5220f8e138..bad81d894c 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -29,14 +29,16 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: return pLeft->addrName.compare(pRight->addrName) < 0; - case PeerTableModel::Subversion: - return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; + case PeerTableModel::Network: + return pLeft->m_network < pRight->m_network; case PeerTableModel::Ping: return pLeft->m_min_ping_usec < pRight->m_min_ping_usec; case PeerTableModel::Sent: return pLeft->nSendBytes < pRight->nSendBytes; case PeerTableModel::Received: return pLeft->nRecvBytes < pRight->nRecvBytes; + case PeerTableModel::Subversion: + return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; } return false; @@ -104,7 +106,6 @@ PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) : m_node(node), timer(nullptr) { - columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); priv.reset(new PeerTablePriv()); // set up timer for auto refresh @@ -133,13 +134,17 @@ void PeerTableModel::stopAutoRefresh() int PeerTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return priv->size(); } int PeerTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return columns.length(); } @@ -158,17 +163,21 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const case Address: // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName); - case Subversion: - return QString::fromStdString(rec->nodeStats.cleanSubVer); + case Network: + return GUIUtil::NetworkToQString(rec->nodeStats.m_network); case Ping: return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec); case Sent: return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); case Received: return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); + case Subversion: + return QString::fromStdString(rec->nodeStats.cleanSubVer); } } else if (role == Qt::TextAlignmentRole) { switch (index.column()) { + case Network: + return QVariant(Qt::AlignCenter); case Ping: case Sent: case Received: @@ -176,6 +185,11 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const default: return QVariant(); } + } else if (role == StatsRole) { + switch (index.column()) { + case NetNodeId: return QVariant::fromValue(rec); + default: return QVariant(); + } } return QVariant(); @@ -211,11 +225,6 @@ QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent return QModelIndex(); } -const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) -{ - return priv->index(idx); -} - void PeerTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 99de772ac0..7bff239507 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -28,6 +28,7 @@ struct CNodeCombinedStats { CNodeStateStats nodeStateStats; bool fNodeStateStatsAvailable; }; +Q_DECLARE_METATYPE(CNodeCombinedStats*) class NodeLessThan { @@ -52,7 +53,6 @@ class PeerTableModel : public QAbstractTableModel public: explicit PeerTableModel(interfaces::Node& node, QObject* parent); ~PeerTableModel(); - const CNodeCombinedStats *getNodeStats(int idx); int getRowByNodeId(NodeId nodeid); void startAutoRefresh(); void stopAutoRefresh(); @@ -60,10 +60,15 @@ public: enum ColumnIndex { NetNodeId = 0, Address = 1, - Ping = 2, - Sent = 3, - Received = 4, - Subversion = 5 + Network = 2, + Ping = 3, + Sent = 4, + Received = 5, + Subversion = 6 + }; + + enum { + StatsRole = Qt::UserRole, }; /** @name Methods overridden from QAbstractTableModel @@ -82,7 +87,7 @@ public Q_SLOTS: private: interfaces::Node& m_node; - QStringList columns; + const QStringList columns{tr("Peer Id"), tr("Address"), tr("Network"), tr("Ping"), tr("Sent"), tr("Received"), tr("User Agent")}; std::unique_ptr<PeerTablePriv> priv; QTimer *timer; }; diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 58167d4bb4..55ab6046cf 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -19,7 +19,7 @@ PSBTOperationsDialog::PSBTOperationsDialog( - QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent), + QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent, GUIUtil::dialog_flags), m_ui(new Ui::PSBTOperationsDialog), m_wallet_model(wallet_model), m_client_model(client_model) diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp index 141f4abb3b..490826cbbb 100644 --- a/src/qt/qrimagewidget.cpp +++ b/src/qt/qrimagewidget.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/qrimagewidget.h b/src/qt/qrimagewidget.h index a031bd7632..5dbba074a1 100644 --- a/src/qt/qrimagewidget.h +++ b/src/qt/qrimagewidget.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index 2c72b2ecda..b32305f5e1 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index d374d610ee..0aea920c86 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -8,6 +8,7 @@ #include <qt/forms/ui_receivecoinsdialog.h> #include <qt/addresstablemodel.h> +#include <qt/guiutil.h> #include <qt/optionsmodel.h> #include <qt/platformstyle.h> #include <qt/receiverequestdialog.h> @@ -21,7 +22,7 @@ #include <QTextDocument> ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::ReceiveCoinsDialog), columnResizingFixer(nullptr), model(nullptr), diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 27455e2906..9b89bd6a8b 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index d385c42821..78ae5c07da 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -19,7 +19,7 @@ #endif ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::ReceiveRequestDialog), model(nullptr) { diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index 846478643d..c861680761 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index b11a1ced00..03531a1381 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -36,15 +36,17 @@ RecentRequestsTableModel::~RecentRequestsTableModel() int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - + if (parent.isValid()) { + return 0; + } return list.length(); } int RecentRequestsTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - + if (parent.isValid()) { + return 0; + } return columns.length(); } diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index c0bd3461bb..b817b64e77 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/res/animation/makespinner.sh b/src/qt/res/animation/makespinner.sh index 4fa8dadf86..83142f5034 100755 --- a/src/qt/res/animation/makespinner.sh +++ b/src/qt/res/animation/makespinner.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-2020 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 236c6e13d5..a252685d2f 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -16,9 +16,10 @@ #include <chainparams.h> #include <interfaces/node.h> #include <netbase.h> -#include <rpc/server.h> #include <rpc/client.h> +#include <rpc/server.h> #include <util/strencodings.h> +#include <util/string.h> #include <util/system.h> #include <util/threadnames.h> @@ -457,7 +458,21 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); } - QChar nonbreaking_hyphen(8209); + ui->splitter->restoreState(settings.value("PeersTabSplitterSizes").toByteArray()); + + constexpr QChar nonbreaking_hyphen(8209); + const std::vector<QString> CONNECTION_TYPE_DOC{ + tr("Inbound Full/Block Relay: initiated by peer"), + tr("Outbound Full Relay: default"), + tr("Outbound Block Relay: does not relay transactions or addresses"), + tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options") + .arg("addnode") + .arg(QString(nonbreaking_hyphen) + "addnode") + .arg(QString(nonbreaking_hyphen) + "connect"), + tr("Outbound Feeler: short-lived, for testing addresses"), + tr("Outbound Address Fetch: short-lived, for soliciting addresses")}; + const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"}; + ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list)); ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir")); ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir")); ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME)); @@ -502,6 +517,7 @@ RPCConsole::~RPCConsole() { QSettings settings; settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); + settings.setValue("PeersTabSplitterSizes", ui->splitter->saveState()); m_node.rpcUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; @@ -1018,11 +1034,9 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) void RPCConsole::peerLayoutAboutToChange() { - QModelIndexList selected = ui->peerWidget->selectionModel()->selectedIndexes(); cachedNodeids.clear(); - for(int i = 0; i < selected.size(); i++) - { - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.at(i).row()); + for (const QModelIndex& peer : GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId)) { + const auto stats = peer.data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); cachedNodeids.append(stats->nodeStats.nodeid); } } @@ -1081,18 +1095,16 @@ void RPCConsole::peerLayoutChanged() void RPCConsole::updateDetailWidget() { - QModelIndexList selected_rows; - auto selection_model = ui->peerWidget->selectionModel(); - if (selection_model) selected_rows = selection_model->selectedRows(); - if (!clientModel || !clientModel->getPeerTableModel() || selected_rows.size() != 1) { + const QList<QModelIndex> selected_peers = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); + if (!clientModel || !clientModel->getPeerTableModel() || selected_peers.size() != 1) { ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); return; } - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected_rows.first().row()); + const auto stats = selected_peers.first().data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); // update the detail ui with latest node information QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " "); - peerAddrDetails += tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid)); + peerAddrDetails += tr("(peer id: %1)").arg(QString::number(stats->nodeStats.nodeid)); if (!stats->nodeStats.addrLocal.empty()) peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal)); ui->peerHeading->setText(peerAddrDetails); @@ -1108,8 +1120,8 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound")); - ui->peerHeight->setText(QString::number(stats->nodeStats.nStartingHeight)); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, stats->nodeStats.fRelayTxes)); + ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == PF_NONE) { ui->peerPermissions->setText(tr("N/A")); } else { @@ -1135,6 +1147,8 @@ void RPCConsole::updateDetailWidget() ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight)); else ui->peerCommonHeight->setText(tr("Unknown")); + + ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height)); } ui->detailWidget->show(); @@ -1200,19 +1214,9 @@ void RPCConsole::banSelectedNode(int bantime) if (!clientModel) return; - // Get selected peer addresses - QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); - for(int i = 0; i < nodes.count(); i++) - { - // Get currently selected peer address - NodeId id = nodes.at(i).data().toLongLong(); - - // Get currently selected peer address - int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id); - if (detailNodeRow < 0) return; - + for (const QModelIndex& peer : GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId)) { // Find possible nodes, ban it and clear the selected node - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); + const auto stats = peer.data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); if (stats) { m_node.ban(stats->nodeStats.addr, bantime); m_node.disconnectByAddress(stats->nodeStats.addr); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 8fea08ab5c..5f308dc36d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 8a62c64d79..611f3584c3 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -35,7 +35,7 @@ #include <QSettings> #include <QTextDocument> -static const std::array<int, 9> confTargets = { {2, 4, 6, 12, 24, 48, 144, 504, 1008} }; +static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008}; int getConfTargetForIndex(int index) { if (index+1 > static_cast<int>(confTargets.size())) { return confTargets.back(); @@ -55,7 +55,7 @@ int getIndexForConfTarget(int target) { } SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::SendCoinsDialog), clientModel(nullptr), model(nullptr), @@ -973,6 +973,9 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); setDefaultButton(QMessageBox::Cancel); yesButton = button(QMessageBox::Yes); + if (confirmButtonText.isEmpty()) { + confirmButtonText = yesButton->text(); + } updateYesButton(); connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown); } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 8519f1f65b..4fc2f57cd6 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -115,7 +115,7 @@ class SendConfirmationDialog : public QMessageBox Q_OBJECT public: - SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr); + SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "", QWidget* parent = nullptr); int exec() override; private Q_SLOTS: diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h index 6619faf417..01135cdfef 100644 --- a/src/qt/sendcoinsrecipient.h +++ b/src/qt/sendcoinsrecipient.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 4835dd7954..2e7df60574 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -19,7 +19,7 @@ #include <QClipboard> SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformStyle, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::SignVerifyMessageDialog), model(nullptr), platformStyle(_platformStyle) @@ -120,7 +120,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - const PKHash* pkhash = boost::get<PKHash>(&destination); + const PKHash* pkhash = std::get_if<PKHash>(&destination); if (!pkhash) { ui->addressIn_SM->setValid(false); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index d33a2d038d..d98cb290a1 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2015 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 5796a6ef56..2292c01d6a 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -18,6 +18,8 @@ #include <util/system.h> #include <util/translation.h> +#include <functional> + #include <QApplication> #include <QCloseEvent> #include <QPainter> diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index d49fd87055..386039291c 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/test/addressbooktests.h b/src/qt/test/addressbooktests.h index 5de89c7592..ab52206466 100644 --- a/src/qt/test/addressbooktests.h +++ b/src/qt/test/addressbooktests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,7 @@ class Node; class AddressBookTests : public QObject { public: - AddressBookTests(interfaces::Node& node) : m_node(node) {} + explicit AddressBookTests(interfaces::Node& node) : m_node(node) {} interfaces::Node& m_node; Q_OBJECT diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index ea7b5f0c9e..0d9928d363 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. @@ -33,7 +33,7 @@ static RPCHelpMan rpcNestedTest_rpc() } static const CRPCCommand vRPCCommands[] = { - {"test", "rpcNestedTest", &rpcNestedTest_rpc, {"arg1", "arg2", "arg3"}}, + {"test", &rpcNestedTest_rpc}, }; void RPCNestedTests::rpcNestedTests() @@ -43,41 +43,41 @@ void RPCNestedTests::rpcNestedTests() tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); TestingSetup test; + m_node.setContext(&test.m_node); if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished(); std::string result; std::string result2; std::string filtered; - interfaces::Node* node = &m_node; - RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path QVERIFY(result=="main"); QVERIFY(filtered == "getblockchaininfo()[chain]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getbestblockhash())"); //simple 2 level nesting - RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getblock(getbestblockhash())[hash], true)"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblock(getbestblockhash())"); //simple 2 level nesting + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblock(getblock(getbestblockhash())[hash], true)"); - RPCConsole::RPCExecuteCommandLine(*node, result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter - RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo"); QVERIFY(result.substr(0,1) == "{"); - RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()"); QVERIFY(result.substr(0,1) == "{"); - RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo "); //whitespace at the end will be tolerated + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo "); //whitespace at the end will be tolerated QVERIFY(result.substr(0,1) == "{"); - (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child containing the quotes in the key + (RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child containing the quotes in the key QVERIFY(result == "null"); - (RPCConsole::RPCExecuteCommandLine(*node, result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed - (RPCConsole::RPCExecuteCommandLine(*node, result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed + (RPCConsole::RPCExecuteCommandLine(m_node, result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed + (RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed QVERIFY(result == result2); - (RPCConsole::RPCExecuteCommandLine(*node, result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed + (RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed QVERIFY(result == result2); - RPCConsole::RPCExecuteCommandLine(*node, result, "getblock(getbestblockhash())[tx][0]", &filtered); + RPCConsole::RPCExecuteCommandLine(m_node, result, "getblock(getbestblockhash())[tx][0]", &filtered); QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]"); @@ -102,35 +102,35 @@ void RPCNestedTests::rpcNestedTests() RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered); QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest"); QVERIFY(result == "[]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest ''"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest ''"); QVERIFY(result == "[\"\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest \"\""); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest \"\""); QVERIFY(result == "[\"\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest '' abc"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest '' abc"); QVERIFY(result == "[\"\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc '' abc"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc '' abc"); QVERIFY(result == "[\"abc\",\"\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc abc"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc abc"); QVERIFY(result == "[\"abc\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc\t\tabc"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc\t\tabc"); QVERIFY(result == "[\"abc\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc )"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc )"); QVERIFY(result == "[\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc )"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest( abc )"); QVERIFY(result == "[\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc , cba )"); + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest( abc , cba )"); QVERIFY(result == "[\"abc\",\"cba\"]"); // do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3) - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax - (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments - (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()()()")); //tolerate non command brackts - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo(True)"), UniValue); //invalid argument - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "a(getblockchaininfo(True))"), UniValue); //method not found - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax + (RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments + (RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()()()")); //tolerate non command brackets + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo(True)"), UniValue); //invalid argument + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "a(getblockchaininfo(True))"), UniValue); //method not found + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tolerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tolerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tolerate empty arguments when using , } diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h index 0a00d1113a..28dc7140e1 100644 --- a/src/qt/test/rpcnestedtests.h +++ b/src/qt/test/rpcnestedtests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. @@ -15,7 +15,7 @@ class Node; class RPCNestedTests : public QObject { public: - RPCNestedTests(interfaces::Node& node) : m_node(node) {} + explicit RPCNestedTests(interfaces::Node& node) : m_node(node) {} interfaces::Node& m_node; Q_OBJECT diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 86356b43c8..9ef5fe8fc7 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/test/util.cpp b/src/qt/test/util.cpp index 759832381b..987d921f03 100644 --- a/src/qt/test/util.cpp +++ b/src/qt/test/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/test/util.h b/src/qt/test/util.h index 950314da3b..df5931a032 100644 --- a/src/qt/test/util.h +++ b/src/qt/test/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/test/wallettests.h b/src/qt/test/wallettests.h index 8ee40bf07f..6044bedb1d 100644 --- a/src/qt/test/wallettests.h +++ b/src/qt/test/wallettests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,7 @@ class Node; class WalletTests : public QObject { public: - WalletTests(interfaces::Node& node) : m_node(node) {} + explicit WalletTests(interfaces::Node& node) : m_node(node) {} interfaces::Node& m_node; Q_OBJECT diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 6428fc4daf..dabdf9d887 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp index 715e312b19..6f31d8463f 100644 --- a/src/qt/transactiondescdialog.cpp +++ b/src/qt/transactiondescdialog.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,7 +11,7 @@ #include <QModelIndex> TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *parent) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::TransactionDescDialog) { ui->setupUi(this); diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index d6bb84f7e6..693b363692 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/transactionoverviewwidget.h b/src/qt/transactionoverviewwidget.h new file mode 100644 index 0000000000..2bdead7bc4 --- /dev/null +++ b/src/qt/transactionoverviewwidget.h @@ -0,0 +1,41 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H +#define BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H + +#include <qt/transactiontablemodel.h> + +#include <QListView> +#include <QSize> +#include <QSizePolicy> + +QT_BEGIN_NAMESPACE +class QShowEvent; +class QWidget; +QT_END_NAMESPACE + +class TransactionOverviewWidget : public QListView +{ + Q_OBJECT + +public: + explicit TransactionOverviewWidget(QWidget* parent = nullptr) : QListView(parent) {} + + QSize sizeHint() const override + { + return {sizeHintForColumn(TransactionTableModel::ToAddress), QListView::sizeHint().height()}; + } + +protected: + void showEvent(QShowEvent* event) override + { + Q_UNUSED(event); + QSizePolicy sp = sizePolicy(); + sp.setHorizontalPolicy(QSizePolicy::Minimum); + setSizePolicy(sp); + } +}; + +#endif // BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 632a18de5c..77fec93f0f 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -123,7 +123,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface continue; } - if (!boost::get<CNoDestination>(&wtx.txout_address[nOut])) + if (!std::get_if<CNoDestination>(&wtx.txout_address[nOut])) { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index c983c527c0..e10243a28a 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 3148089b52..e6d483364c 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -19,6 +19,7 @@ #include <uint256.h> #include <algorithm> +#include <functional> #include <QColor> #include <QDateTime> @@ -289,13 +290,17 @@ void TransactionTableModel::updateConfirmations() int TransactionTableModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return priv->size(); } int TransactionTableModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); + if (parent.isValid()) { + return 0; + } return columns.length(); } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 4b699d4d7d..f8576edd59 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index e14e22e9de..0cf6480b82 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 9ce7f4ad97..b268823066 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index b7f85446f4..05499b2a41 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -29,7 +29,7 @@ /** "Help message" or "About" dialog box */ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : - QDialog(parent), + QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::HelpMessageDialog) { ui->setupUi(this); diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index d9e0274d01..7a89325ddf 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -128,10 +128,20 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal } // Instantiate model and register it. - WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style, nullptr); - // Handler callback runs in a different thread so fix wallet model thread affinity. + WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style, + nullptr /* required for the following moveToThread() call */); + + // Move WalletModel object to the thread that created the WalletController + // object (GUI main thread), instead of the current thread, which could be + // an outside wallet thread or RPC thread sending a LoadWallet notification. + // This ensures queued signals sent to the WalletModel object will be + // handled on the GUI event loop. wallet_model->moveToThread(thread()); - wallet_model->setParent(this); + // setParent(parent) must be called in the thread which created the parent object. More details in #18948. + GUIUtil::ObjectInvoke(this, [wallet_model, this] { + wallet_model->setParent(this); + }, GUIUtil::blockingGUIThreadConnection()); + m_wallets.push_back(wallet_model); // WalletModel::startPollBalance needs to be called in a thread managed by @@ -142,7 +152,7 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal connect(wallet_model, &WalletModel::unload, this, [this, wallet_model] { // Defer removeAndDeleteWallet when no modal widget is active. - // TODO: remove this workaround by removing usage of QDiallog::exec. + // TODO: remove this workaround by removing usage of QDialog::exec. if (QApplication::activeModalWidget()) { connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() { if (!QApplication::activeModalWidget()) { @@ -157,7 +167,6 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal // Re-emit coinsSent signal from wallet model. connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent); - // Notify walletAdded signal on the GUI thread. Q_EMIT walletAdded(wallet_model); return wallet_model; diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 2edb7eff8a..02b3c62867 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -106,9 +106,24 @@ void WalletFrame::setCurrentWallet(WalletModel* wallet_model) { if (mapWalletViews.count(wallet_model) == 0) return; + // Stop the effect of hidden widgets on the size hint of the shown one in QStackedWidget. + WalletView* view_about_to_hide = currentWalletView(); + if (view_about_to_hide) { + QSizePolicy sp = view_about_to_hide->sizePolicy(); + sp.setHorizontalPolicy(QSizePolicy::Ignored); + view_about_to_hide->setSizePolicy(sp); + } + WalletView *walletView = mapWalletViews.value(wallet_model); - walletStack->setCurrentWidget(walletView); assert(walletView); + + // Set or restore the default QSizePolicy which could be set to QSizePolicy::Ignored previously. + QSizePolicy sp = walletView->sizePolicy(); + sp.setHorizontalPolicy(QSizePolicy::Preferred); + walletView->setSizePolicy(sp); + walletView->updateGeometry(); + + walletStack->setCurrentWidget(walletView); walletView->updateEncryptionStatus(); } diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 271ddfc6c2..f57f8678d6 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 907444bff8..02254da3ce 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -29,6 +29,7 @@ #include <wallet/wallet.h> // for CRecipient #include <stdint.h> +#include <functional> #include <QDebug> #include <QMessageBox> @@ -514,6 +515,13 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) questionString.append("</td><td>"); questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee)); questionString.append("</td></tr></table>"); + + // Display warning in the "Confirm fee bump" window if the "Coin Control Features" option is enabled + if (getOptionsModel()->getCoinControlFeatures()) { + questionString.append("<br><br>"); + questionString.append(tr("Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy.")); + } + SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString); confirmationDialog.exec(); QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result()); diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 5e07c3db40..9248db1539 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -69,7 +69,7 @@ void RandAddSeedPerfmon(CSHA512& hasher) // 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}}; + static std::atomic<std::chrono::seconds> last_perfmon{0s}; auto last_time = last_perfmon.load(); auto current_time = GetTime<std::chrono::seconds>(); if (current_time < last_time + std::chrono::minutes{10}) return; diff --git a/src/rest.cpp b/src/rest.cpp index 949cc9d84a..f872f6e59d 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -179,7 +179,7 @@ static bool rest_headers(const util::Ref& context, { LOCK(cs_main); tip = ::ChainActive().Tip(); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); while (pindex != nullptr && ::ChainActive().Contains(pindex)) { headers.push_back(pindex); if (headers.size() == (unsigned long)count) @@ -247,7 +247,7 @@ static bool rest_block(HTTPRequest* req, { LOCK(cs_main); tip = ::ChainActive().Tip(); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 392073d047..a83b45ae1d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -17,6 +17,7 @@ #include <node/coinstats.h> #include <node/context.h> #include <node/utxo_snapshot.h> +#include <policy/fees.h> #include <policy/feerate.h> #include <policy/policy.h> #include <policy/rbf.h> @@ -65,7 +66,7 @@ NodeContext& EnsureNodeContext(const util::Ref& context) CTxMemPool& EnsureMemPool(const util::Ref& context) { - NodeContext& node = EnsureNodeContext(context); + const NodeContext& node = EnsureNodeContext(context); if (!node.mempool) { throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); } @@ -74,13 +75,22 @@ CTxMemPool& EnsureMemPool(const util::Ref& context) ChainstateManager& EnsureChainman(const util::Ref& context) { - NodeContext& node = EnsureNodeContext(context); + const NodeContext& node = EnsureNodeContext(context); if (!node.chainman) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found"); } return *node.chainman; } +CBlockPolicyEstimator& EnsureFeeEstimator(const util::Ref& context) +{ + NodeContext& node = EnsureNodeContext(context); + if (!node.fee_estimator) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled"); + } + return *node.fee_estimator; +} + /* Calculate the difficulty for a given block index. */ double GetDifficulty(const CBlockIndex* blockindex) @@ -162,16 +172,21 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn result.pushKV("versionHex", strprintf("%08x", block.nVersion)); result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); UniValue txs(UniValue::VARR); - for(const auto& tx : block.vtx) - { - if(txDetails) - { + if (txDetails) { + CBlockUndo blockUndo; + const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex); + for (size_t i = 0; i < block.vtx.size(); ++i) { + const CTransactionRef& tx = block.vtx.at(i); + // coinbase transaction (i == 0) doesn't have undo data + const CTxUndo* txundo = (have_undo && i) ? &blockUndo.vtxundo.at(i - 1) : nullptr; UniValue objTx(UniValue::VOBJ); - TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags()); + TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo); txs.push_back(objTx); } - else + } else { + for (const CTransactionRef& tx : block.vtx) { txs.push_back(tx->GetHash().GetHex()); + } } result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); @@ -828,7 +843,7 @@ static RPCHelpMan getblockheader() const CBlockIndex* tip; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); tip = ::ChainActive().Tip(); } @@ -926,6 +941,7 @@ static RPCHelpMan getblock() {RPCResult::Type::OBJ, "", "", { {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"}, + {RPCResult::Type::NUM, "fee", "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"}, }}, }}, }}, @@ -951,7 +967,7 @@ static RPCHelpMan getblock() const CBlockIndex* tip; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); tip = ::ChainActive().Tip(); if (!pblockindex) { @@ -1148,7 +1164,7 @@ static RPCHelpMan gettxout() } } - const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock()); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(coins_view->GetBestBlock()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.nHeight == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); @@ -1267,7 +1283,7 @@ RPCHelpMan getblockchaininfo() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"}, + {RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"}, {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"}, {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"}, {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"}, @@ -1541,7 +1557,7 @@ static RPCHelpMan preciousblock() { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -1579,7 +1595,7 @@ static RPCHelpMan invalidateblock() CBlockIndex* pblockindex; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -1587,7 +1603,7 @@ static RPCHelpMan invalidateblock() InvalidateBlock(state, Params(), pblockindex); if (state.IsValid()) { - ActivateBestChain(state, Params()); + ::ChainstateActive().ActivateBestChain(state, Params()); } if (!state.IsValid()) { @@ -1618,7 +1634,7 @@ static RPCHelpMan reconsiderblock() { LOCK(cs_main); - CBlockIndex* pblockindex = LookupBlockIndex(hash); + CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -1627,7 +1643,7 @@ static RPCHelpMan reconsiderblock() } BlockValidationState state; - ActivateBestChain(state, Params()); + ::ChainstateActive().ActivateBestChain(state, Params()); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); @@ -1673,7 +1689,7 @@ static RPCHelpMan getchaintxstats() } else { uint256 hash(ParseHashV(request.params[1], "blockhash")); LOCK(cs_main); - pindex = LookupBlockIndex(hash); + pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -1851,7 +1867,7 @@ static RPCHelpMan getblockstats() pindex = ::ChainActive()[height]; } else { const uint256 hash(ParseHashV(request.params[0], "hash_or_height")); - pindex = LookupBlockIndex(hash); + pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2314,7 +2330,7 @@ static RPCHelpMan getblockfilter() bool block_was_connected; { LOCK(cs_main); - block_index = LookupBlockIndex(block_hash); + block_index = g_chainman.m_blockman.LookupBlockIndex(block_hash); if (!block_index) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2383,10 +2399,10 @@ static RPCHelpMan dumptxoutset() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - fs::path path = fs::absolute(request.params[0].get_str(), GetDataDir()); + const fs::path path = fsbridge::AbsPathJoin(GetDataDir(), request.params[0].get_str()); // Write to a temporary path and then move into `path` on completion // to avoid confusion due to an interruption. - fs::path temppath = fs::absolute(request.params[0].get_str() + ".incomplete", GetDataDir()); + const fs::path temppath = fsbridge::AbsPathJoin(GetDataDir(), request.params[0].get_str() + ".incomplete"); if (fs::exists(path)) { throw JSONRPCError( @@ -2424,7 +2440,7 @@ static RPCHelpMan dumptxoutset() } pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor()); - tip = LookupBlockIndex(stats.hashBlock); + tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock); CHECK_NONFATAL(tip); } @@ -2464,41 +2480,41 @@ void RegisterBlockchainRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "blockchain", "getblockchaininfo", &getblockchaininfo, {} }, - { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, - { "blockchain", "getblockstats", &getblockstats, {"hash_or_height", "stats"} }, - { "blockchain", "getbestblockhash", &getbestblockhash, {} }, - { "blockchain", "getblockcount", &getblockcount, {} }, - { "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} }, - { "blockchain", "getblockhash", &getblockhash, {"height"} }, - { "blockchain", "getblockheader", &getblockheader, {"blockhash","verbose"} }, - { "blockchain", "getchaintips", &getchaintips, {} }, - { "blockchain", "getdifficulty", &getdifficulty, {} }, - { "blockchain", "getmempoolancestors", &getmempoolancestors, {"txid","verbose"} }, - { "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} }, - { "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, {} }, - { "blockchain", "getrawmempool", &getrawmempool, {"verbose", "mempool_sequence"} }, - { "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type"} }, - { "blockchain", "pruneblockchain", &pruneblockchain, {"height"} }, - { "blockchain", "savemempool", &savemempool, {} }, - { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, - - { "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, - { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} }, - { "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} }, +{ // category actor (function) + // --------------------- ------------------------ + { "blockchain", &getblockchaininfo, }, + { "blockchain", &getchaintxstats, }, + { "blockchain", &getblockstats, }, + { "blockchain", &getbestblockhash, }, + { "blockchain", &getblockcount, }, + { "blockchain", &getblock, }, + { "blockchain", &getblockhash, }, + { "blockchain", &getblockheader, }, + { "blockchain", &getchaintips, }, + { "blockchain", &getdifficulty, }, + { "blockchain", &getmempoolancestors, }, + { "blockchain", &getmempooldescendants, }, + { "blockchain", &getmempoolentry, }, + { "blockchain", &getmempoolinfo, }, + { "blockchain", &getrawmempool, }, + { "blockchain", &gettxout, }, + { "blockchain", &gettxoutsetinfo, }, + { "blockchain", &pruneblockchain, }, + { "blockchain", &savemempool, }, + { "blockchain", &verifychain, }, + + { "blockchain", &preciousblock, }, + { "blockchain", &scantxoutset, }, + { "blockchain", &getblockfilter, }, /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, {"blockhash"} }, - { "hidden", "reconsiderblock", &reconsiderblock, {"blockhash"} }, - { "hidden", "waitfornewblock", &waitfornewblock, {"timeout"} }, - { "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} }, - { "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, - { "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} }, - { "hidden", "dumptxoutset", &dumptxoutset, {"path"} }, + { "hidden", &invalidateblock, }, + { "hidden", &reconsiderblock, }, + { "hidden", &waitfornewblock, }, + { "hidden", &waitforblock, }, + { "hidden", &waitforblockheight, }, + { "hidden", &syncwithvalidationinterfacequeue, }, + { "hidden", &dumptxoutset, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 5b362bf211..e4ce80400e 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,6 +15,7 @@ extern RecursiveMutex cs_main; class CBlock; class CBlockIndex; +class CBlockPolicyEstimator; class CTxMemPool; class ChainstateManager; class UniValue; @@ -54,5 +55,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], NodeContext& EnsureNodeContext(const util::Ref& context); CTxMemPool& EnsureMemPool(const util::Ref& context); ChainstateManager& EnsureChainman(const util::Ref& context); +CBlockPolicyEstimator& EnsureFeeEstimator(const util::Ref& context); #endif diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 042005b9a6..d1eb849b7e 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -210,14 +210,9 @@ public: CRPCConvertTable::CRPCConvertTable() { - const unsigned int n_elem = - (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); - - for (unsigned int i = 0; i < n_elem; i++) { - members.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramIdx)); - membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramName)); + for (const auto& cp : vRPCConvertParams) { + members.emplace(cp.methodName, cp.paramIdx); + membersByName.emplace(cp.methodName, cp.paramName); } } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 7d45ad9434..50987a735b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -375,7 +375,7 @@ static RPCHelpMan generateblock() LOCK(cs_main); BlockValidationState state; - if (!TestBlockValidity(state, chainparams, block, LookupBlockIndex(block.hashPrevBlock), false, false)) { + if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString())); } } @@ -409,7 +409,7 @@ static RPCHelpMan getmininginfo() {RPCResult::Type::NUM, "difficulty", "The current difficulty"}, {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"}, {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"}, - {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"}, + {RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"}, {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, }}, RPCExamples{ @@ -618,7 +618,7 @@ static RPCHelpMan getblocktemplate() throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); uint256 hash = block.GetHash(); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) return "duplicate"; @@ -632,7 +632,7 @@ static RPCHelpMan getblocktemplate() if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; BlockValidationState state; - TestBlockValidity(state, Params(), block, pindexPrev, false, true); + TestBlockValidity(state, Params(), ::ChainstateActive(), block, pindexPrev, false, true); return BIP22ValidationResult(state); } @@ -658,11 +658,15 @@ static RPCHelpMan getblocktemplate() if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); + if (!Params().IsTestChain()) { + if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); + } - if (::ChainstateActive().IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); + if (::ChainstateActive().IsInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); + } + } static unsigned int nTransactionsUpdatedLast; const CTxMemPool& mempool = EnsureMemPool(request.context); @@ -714,6 +718,13 @@ static RPCHelpMan getblocktemplate() // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? } + const Consensus::Params& consensusParams = Params().GetConsensus(); + + // GBT must be called with 'signet' set in the rules for signet chains + if (consensusParams.signet_blocks && setClientRules.count("signet") != 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the signet rule set (call with {\"rules\": [\"segwit\", \"signet\"]})"); + } + // GBT must be called with 'segwit' set in the rules if (setClientRules.count("segwit") != 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})"); @@ -745,7 +756,6 @@ static RPCHelpMan getblocktemplate() } CHECK_NONFATAL(pindexPrev); CBlock* pblock = &pblocktemplate->block; // pointer for convenience - const Consensus::Params& consensusParams = Params().GetConsensus(); // Update nTime UpdateTime(pblock, consensusParams, pindexPrev); @@ -809,6 +819,12 @@ static RPCHelpMan getblocktemplate() UniValue aRules(UniValue::VARR); aRules.push_back("csv"); if (!fPreSegWit) aRules.push_back("!segwit"); + if (consensusParams.signet_blocks) { + // indicate to miner that they must understand signet rules + // when attempting to mine with this template + aRules.push_back("!signet"); + } + UniValue vbavailable(UniValue::VOBJ); for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { Consensus::DeploymentPos pos = Consensus::DeploymentPos(j); @@ -889,6 +905,10 @@ static RPCHelpMan getblocktemplate() result.pushKV("bits", strprintf("%08x", pblock->nBits)); result.pushKV("height", (int64_t)(pindexPrev->nHeight+1)); + if (consensusParams.signet_blocks) { + result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge)); + } + if (!pblocktemplate->vchCoinbaseCommitment.empty()) { result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment)); } @@ -946,7 +966,7 @@ static RPCHelpMan submitblock() uint256 hash = block.GetHash(); { LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) { return "duplicate"; @@ -959,7 +979,7 @@ static RPCHelpMan submitblock() { LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(block.hashPrevBlock); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock); if (pindex) { UpdateUncommittedBlockStructures(block, pindex, Params().GetConsensus()); } @@ -1003,7 +1023,7 @@ static RPCHelpMan submitheader() } { LOCK(cs_main); - if (!LookupBlockIndex(h.hashPrevBlock)) { + if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) { throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first"); } } @@ -1022,21 +1042,19 @@ static RPCHelpMan submitheader() static 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" - "in BIP 141 (witness data is discounted).\n", - { - {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"}, - {"estimate_mode", RPCArg::Type::STR, /* default */ "CONSERVATIVE", "The fee estimate mode.\n" + "\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" + "in BIP 141 (witness data is discounted).\n", + { + {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"}, + {"estimate_mode", RPCArg::Type::STR, /* default */ "conservative", "The fee estimate mode.\n" " Whether to return a more conservative estimate which also satisfies\n" " a longer history. A conservative estimate potentially returns a\n" " higher feerate and is more likely to be sufficient for the desired\n" " target, but is not as responsive to short term drops in the\n" - " prevailing fee market. Must be one of:\n" - " \"UNSET\"\n" - " \"ECONOMICAL\"\n" - " \"CONSERVATIVE\""}, + " prevailing fee market. Must be one of (case insensitive):\n" + "\"" + FeeModes("\"\n\"") + "\""}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -1059,7 +1077,10 @@ static RPCHelpMan estimatesmartfee() { RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR}); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); - unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + + CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(request.context); + + unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target); bool conservative = true; if (!request.params[1].isNull()) { @@ -1073,7 +1094,7 @@ static RPCHelpMan estimatesmartfee() UniValue result(UniValue::VOBJ); UniValue errors(UniValue::VARR); FeeCalculation feeCalc; - CFeeRate feeRate = ::feeEstimator.estimateSmartFee(conf_target, &feeCalc, conservative); + CFeeRate feeRate = fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative); if (feeRate != CFeeRate(0)) { result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK())); } else { @@ -1144,7 +1165,10 @@ static RPCHelpMan estimaterawfee() { RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); - unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + + CBlockPolicyEstimator& fee_estimator = EnsureFeeEstimator(request.context); + + unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target); double threshold = 0.95; if (!request.params[1].isNull()) { @@ -1156,14 +1180,14 @@ static RPCHelpMan estimaterawfee() UniValue result(UniValue::VOBJ); - for (const FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { + for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) { CFeeRate feeRate; EstimationResult buckets; // Only output results for horizons which track the target - if (conf_target > ::feeEstimator.HighestTargetTracked(horizon)) continue; + if (conf_target > fee_estimator.HighestTargetTracked(horizon)) continue; - feeRate = ::feeEstimator.estimateRawFee(conf_target, threshold, horizon, &buckets); + feeRate = fee_estimator.estimateRawFee(conf_target, threshold, horizon, &buckets); UniValue horizon_result(UniValue::VOBJ); UniValue errors(UniValue::VARR); UniValue passbucket(UniValue::VOBJ); @@ -1208,24 +1232,24 @@ void RegisterMiningRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "mining", "getnetworkhashps", &getnetworkhashps, {"nblocks","height"} }, - { "mining", "getmininginfo", &getmininginfo, {} }, - { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} }, - { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, - { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, - { "mining", "submitheader", &submitheader, {"hexdata"} }, +{ // category actor (function) + // --------------------- ----------------------- + { "mining", &getnetworkhashps, }, + { "mining", &getmininginfo, }, + { "mining", &prioritisetransaction, }, + { "mining", &getblocktemplate, }, + { "mining", &submitblock, }, + { "mining", &submitheader, }, - { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "generating", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} }, - { "generating", "generateblock", &generateblock, {"output","transactions"} }, + { "generating", &generatetoaddress, }, + { "generating", &generatetodescriptor, }, + { "generating", &generateblock, }, - { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, + { "util", &estimatesmartfee, }, - { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, - { "hidden", "generate", &generate, {} }, + { "hidden", &estimaterawfee, }, + { "hidden", &generate, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index b3102a236d..b75a7b8d26 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -39,13 +39,14 @@ static RPCHelpMan validateaddress() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not. If not, this is the only property returned."}, + {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, {RPCResult::Type::STR, "address", "The bitcoin address validated"}, {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"}, {RPCResult::Type::BOOL, "isscript", "If the key is a script"}, {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"}, {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, "error", /* optional */ true, "Error message, if any"}, } }, RPCExamples{ @@ -54,13 +55,14 @@ static RPCHelpMan validateaddress() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - CTxDestination dest = DecodeDestination(request.params[0].get_str()); - bool isValid = IsValidDestination(dest); + std::string error_msg; + CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg); + const bool isValid = IsValidDestination(dest); + CHECK_NONFATAL(isValid == error_msg.empty()); UniValue ret(UniValue::VOBJ); ret.pushKV("isvalid", isValid); - if (isValid) - { + if (isValid) { std::string currentAddress = EncodeDestination(dest); ret.pushKV("address", currentAddress); @@ -69,7 +71,10 @@ static RPCHelpMan validateaddress() UniValue detail = DescribeAddress(dest); ret.pushKVs(detail); + } else { + ret.pushKV("error", error_msg); } + return ret; }, }; @@ -694,23 +699,23 @@ void RegisterMiscRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, - { "control", "logging", &logging, {"include", "exclude"}}, - { "util", "validateaddress", &validateaddress, {"address"} }, - { "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} }, - { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "range"} }, - { "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} }, - { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, - { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, - { "util", "getindexinfo", &getindexinfo, {"index_name"} }, +{ // category actor (function) + // --------------------- ------------------------ + { "control", &getmemoryinfo, }, + { "control", &logging, }, + { "util", &validateaddress, }, + { "util", &createmultisig, }, + { "util", &deriveaddresses, }, + { "util", &getdescriptorinfo, }, + { "util", &verifymessage, }, + { "util", &signmessagewithprivkey, }, + { "util", &getindexinfo, }, /* 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", &echojson, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, + { "hidden", &setmocktime, }, + { "hidden", &mockscheduler, }, + { "hidden", &echo, }, + { "hidden", &echojson, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e72ef24d12..47d77b341a 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -5,6 +5,7 @@ #include <rpc/server.h> #include <banman.h> +#include <chainparams.h> #include <clientversion.h> #include <core_io.h> #include <net.h> @@ -126,21 +127,19 @@ static RPCHelpMan getpeerinfo() {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\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::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"}, + {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"}, {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"}, {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"}, {RPCResult::Type::ARR, "inflight", "", { {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, }}, - {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::ARR, "permissions", "Any special permissions that have been granted to this peer", + { + {RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"}, + }}, {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", { @@ -155,6 +154,9 @@ static RPCHelpMan getpeerinfo() "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+"'."} }}, + {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."}, }}, }}, }, @@ -165,8 +167,9 @@ static RPCHelpMan getpeerinfo() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { NodeContext& node = EnsureNodeContext(request.context); - if(!node.connman) + if(!node.connman || !node.peerman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } std::vector<CNodeStats> vstats; node.connman->GetNodeStats(vstats); @@ -176,7 +179,7 @@ static RPCHelpMan getpeerinfo() for (const CNodeStats& stats : vstats) { UniValue obj(UniValue::VOBJ); CNodeStateStats statestats; - bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); + bool fStateStats = node.peerman->GetNodeStateStats(stats.nodeid, statestats); obj.pushKV("id", stats.nodeid); obj.pushKV("addr", stats.addrName); if (stats.addrBind.IsValid()) { @@ -185,7 +188,7 @@ static RPCHelpMan getpeerinfo() if (!(stats.addrLocal.empty())) { obj.pushKV("addrlocal", stats.addrLocal); } - obj.pushKV("network", stats.m_network); + obj.pushKV("network", GetNetworkName(stats.m_network)); if (stats.m_mapped_as != 0) { obj.pushKV("mapped_as", uint64_t(stats.m_mapped_as)); } @@ -215,16 +218,10 @@ static RPCHelpMan getpeerinfo() // their ver message. obj.pushKV("subver", stats.cleanSubVer); obj.pushKV("inbound", stats.fInbound); - 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); + obj.pushKV("bip152_hb_to", stats.m_bip152_highbandwidth_to); + obj.pushKV("bip152_hb_from", stats.m_bip152_highbandwidth_from); if (fStateStats) { - if (IsDeprecatedRPCEnabled("banscore")) { - // banscore is deprecated in v0.21 for removal in v0.22 - obj.pushKV("banscore", statestats.m_misbehavior_score); - } + obj.pushKV("startingheight", statestats.m_starting_height); obj.pushKV("synced_headers", statestats.nSyncHeight); obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); @@ -233,10 +230,6 @@ static RPCHelpMan getpeerinfo() } obj.pushKV("inflight", heights); } - 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); @@ -257,7 +250,7 @@ static RPCHelpMan getpeerinfo() recvPerMsgCmd.pushKV(i.first, i.second); } obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd); - obj.pushKV("connection_type", stats.m_conn_type_string); + obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type)); ret.push_back(obj); } @@ -322,6 +315,61 @@ static RPCHelpMan addnode() }; } +static RPCHelpMan addconnection() +{ + return RPCHelpMan{"addconnection", + "\nOpen an outbound connection to a specified node. This RPC is for testing only.\n", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address and port to attempt connecting to."}, + {"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open, either \"outbound-full-relay\" or \"block-relay-only\"."}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + { RPCResult::Type::STR, "address", "Address of newly added connection." }, + { RPCResult::Type::STR, "connection_type", "Type of connection opened." }, + }}, + RPCExamples{ + HelpExampleCli("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\"") + + HelpExampleRpc("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + if (Params().NetworkIDString() != CBaseChainParams::REGTEST) { + throw std::runtime_error("addconnection is for regression testing (-regtest mode) only."); + } + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VSTR}); + const std::string address = request.params[0].get_str(); + const std::string conn_type_in{TrimString(request.params[1].get_str())}; + ConnectionType conn_type{}; + if (conn_type_in == "outbound-full-relay") { + conn_type = ConnectionType::OUTBOUND_FULL_RELAY; + } else if (conn_type_in == "block-relay-only") { + conn_type = ConnectionType::BLOCK_RELAY; + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString()); + } + + NodeContext& node = EnsureNodeContext(request.context); + if (!node.connman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled."); + } + + const bool success = node.connman->AddConnection(address, conn_type); + if (!success) { + throw JSONRPCError(RPC_CLIENT_NODE_CAPACITY_REACHED, "Error: Already at capacity for specified connection type."); + } + + UniValue info(UniValue::VOBJ); + info.pushKV("address", address); + info.pushKV("connection_type", conn_type_in); + + return info; +}, + }; +} + static RPCHelpMan disconnectnode() { return RPCHelpMan{"disconnectnode", @@ -482,12 +530,12 @@ static RPCHelpMan getnettotals() obj.pushKV("timemillis", GetTimeMillis()); UniValue outboundLimit(UniValue::VOBJ); - outboundLimit.pushKV("timeframe", node.connman->GetMaxOutboundTimeframe()); + outboundLimit.pushKV("timeframe", count_seconds(node.connman->GetMaxOutboundTimeframe())); outboundLimit.pushKV("target", node.connman->GetMaxOutboundTarget()); outboundLimit.pushKV("target_reached", node.connman->OutboundTargetReached(false)); outboundLimit.pushKV("serve_historical_blocks", !node.connman->OutboundTargetReached(true)); outboundLimit.pushKV("bytes_left_in_cycle", node.connman->GetOutboundTargetBytesLeft()); - outboundLimit.pushKV("time_left_in_cycle", node.connman->GetMaxOutboundTimeLeftInCycle()); + outboundLimit.pushKV("time_left_in_cycle", count_seconds(node.connman->GetMaxOutboundTimeLeftInCycle())); obj.pushKV("uploadtarget", outboundLimit); return obj; }, @@ -577,7 +625,9 @@ static RPCHelpMan getnetworkinfo() obj.pushKV("localservices", strprintf("%016x", services)); obj.pushKV("localservicesnames", GetServicesNames(services)); } - obj.pushKV("localrelay", g_relay_txes); + if (node.peerman) { + obj.pushKV("localrelay", !node.peerman->IgnoresIncomingTxs()); + } obj.pushKV("timeoffset", GetTimeOffset()); if (node.connman) { obj.pushKV("networkactive", node.connman->GetNetworkActive()); @@ -891,22 +941,24 @@ void RegisterNetRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "network", "getconnectioncount", &getconnectioncount, {} }, - { "network", "ping", &ping, {} }, - { "network", "getpeerinfo", &getpeerinfo, {} }, - { "network", "addnode", &addnode, {"node","command"} }, - { "network", "disconnectnode", &disconnectnode, {"address", "nodeid"} }, - { "network", "getaddednodeinfo", &getaddednodeinfo, {"node"} }, - { "network", "getnettotals", &getnettotals, {} }, - { "network", "getnetworkinfo", &getnetworkinfo, {} }, - { "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} }, - { "network", "listbanned", &listbanned, {} }, - { "network", "clearbanned", &clearbanned, {} }, - { "network", "setnetworkactive", &setnetworkactive, {"state"} }, - { "network", "getnodeaddresses", &getnodeaddresses, {"count"} }, - { "hidden", "addpeeraddress", &addpeeraddress, {"address", "port"} }, +{ // category actor + // --------------------- ----------------------- + { "network", &getconnectioncount, }, + { "network", &ping, }, + { "network", &getpeerinfo, }, + { "network", &addnode, }, + { "network", &disconnectnode, }, + { "network", &getaddednodeinfo, }, + { "network", &getnettotals, }, + { "network", &getnetworkinfo, }, + { "network", &setban, }, + { "network", &listbanned, }, + { "network", &clearbanned, }, + { "network", &setnetworkactive, }, + { "network", &getnodeaddresses, }, + + { "hidden", &addconnection, }, + { "hidden", &addpeeraddress, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index d1475f452d..fc00a1efad 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -62,6 +62,7 @@ enum RPCErrorCode RPC_CLIENT_NODE_NOT_CONNECTED = -29, //!< Node to disconnect not found in connected nodes RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //!< Invalid IP/Subnet RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found + RPC_CLIENT_NODE_CAPACITY_REACHED= -34, //!< Max number of outbound or block-relay connections already open //! Chain errors RPC_CLIENT_MEMPOOL_DISABLED = -33, //!< No mempool instance found @@ -78,6 +79,7 @@ enum RPCErrorCode RPC_WALLET_ALREADY_UNLOCKED = -17, //!< Wallet is already unlocked RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded) + RPC_WALLET_ALREADY_LOADED = -35, //!< This same wallet is already loaded //! Backwards compatible aliases RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 8cf98341b1..ac42404470 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -54,7 +54,7 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& LOCK(cs_main); entry.pushKV("blockhash", hashBlock.GetHex()); - CBlockIndex* pindex = LookupBlockIndex(hashBlock); + CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (pindex) { if (::ChainActive().Contains(pindex)) { entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight); @@ -109,7 +109,7 @@ static RPCHelpMan getrawtransaction() {RPCResult::Type::OBJ, "", "", { {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, - {RPCResult::Type::STR, "vout", ""}, + {RPCResult::Type::NUM, "vout", "The output number"}, {RPCResult::Type::OBJ, "scriptSig", "The script", { {RPCResult::Type::STR, "asm", "asm"}, @@ -178,7 +178,7 @@ static RPCHelpMan getrawtransaction() LOCK(cs_main); uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); - blockindex = LookupBlockIndex(blockhash); + blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash); if (!blockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); } @@ -244,16 +244,15 @@ static RPCHelpMan gettxoutproof() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { std::set<uint256> setTxids; - uint256 oneTxid; UniValue txids = request.params[0].get_array(); + if (txids.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter 'txids' cannot be empty"); + } for (unsigned int idx = 0; idx < txids.size(); idx++) { - const UniValue& txid = txids[idx]; - uint256 hash(ParseHashV(txid, "txid")); - if (setTxids.count(hash)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + txid.get_str()); + auto ret = setTxids.insert(ParseHashV(txids[idx], "txid")); + if (!ret.second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ") + txids[idx].get_str()); } - setTxids.insert(hash); - oneTxid = hash; } CBlockIndex* pblockindex = nullptr; @@ -261,7 +260,7 @@ static RPCHelpMan gettxoutproof() if (!request.params[1].isNull()) { LOCK(cs_main); hashBlock = ParseHashV(request.params[1], "blockhash"); - pblockindex = LookupBlockIndex(hashBlock); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -287,11 +286,11 @@ static RPCHelpMan gettxoutproof() LOCK(cs_main); if (pblockindex == nullptr) { - const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, /* mempool */ nullptr, oneTxid, Params().GetConsensus(), hashBlock); + const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, /* mempool */ nullptr, *setTxids.begin(), Params().GetConsensus(), hashBlock); if (!tx || hashBlock.IsNull()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); } - pblockindex = LookupBlockIndex(hashBlock); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); } @@ -351,7 +350,7 @@ static RPCHelpMan verifytxoutproof() LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash()); if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } @@ -817,10 +816,11 @@ static 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" + "\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n" "for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n" "nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n" - "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n", + "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_CHAIN, may throw if the transaction cannot be added to the mempool.\n" + "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n", { {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"}, {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), @@ -895,6 +895,7 @@ static RPCHelpMan testmempoolaccept() {RPCResult::Type::OBJ, "", "", { {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, + {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"}, {RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"}, {RPCResult::Type::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)", @@ -931,7 +932,6 @@ static RPCHelpMan testmempoolaccept() 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(); const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ? DEFAULT_MAX_RAW_TX_FEE_RATE : @@ -943,7 +943,8 @@ static RPCHelpMan testmempoolaccept() UniValue result(UniValue::VARR); UniValue result_0(UniValue::VOBJ); - result_0.pushKV("txid", tx_hash.GetHex()); + result_0.pushKV("txid", tx->GetHash().GetHex()); + result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex()); TxValidationState state; bool test_accept_res; @@ -1859,27 +1860,27 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, - { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} }, - { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","maxfeerate"} }, - { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, - { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, - { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","maxfeerate"} }, - { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, - { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} }, - { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} }, - { "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} }, - { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} }, - { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt", "descriptors"} }, - { "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} }, - { "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} }, - - { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, - { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, +{ // category actor (function) + // --------------------- ----------------------- + { "rawtransactions", &getrawtransaction, }, + { "rawtransactions", &createrawtransaction, }, + { "rawtransactions", &decoderawtransaction, }, + { "rawtransactions", &decodescript, }, + { "rawtransactions", &sendrawtransaction, }, + { "rawtransactions", &combinerawtransaction, }, + { "rawtransactions", &signrawtransactionwithkey, }, + { "rawtransactions", &testmempoolaccept, }, + { "rawtransactions", &decodepsbt, }, + { "rawtransactions", &combinepsbt, }, + { "rawtransactions", &finalizepsbt, }, + { "rawtransactions", &createpsbt, }, + { "rawtransactions", &converttopsbt, }, + { "rawtransactions", &utxoupdatepsbt, }, + { "rawtransactions", &joinpsbts, }, + { "rawtransactions", &analyzepsbt, }, + + { "blockchain", &gettxoutproof, }, + { "blockchain", &verifytxoutproof, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index f004ecc20c..122a92f084 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -286,7 +286,7 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, SignTransactionResultToJSON(mtx, complete, coins, input_errors, result); } -void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result) +void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, std::string>& input_errors, UniValue& result) { // Make errors UniValue UniValue vErrors(UniValue::VARR); diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index 942314eccf..ce7d5834fa 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -25,7 +25,7 @@ class SigningProvider; * @param result JSON object where signed transaction results accumulate */ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result); -void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result); +void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, std::string>& input_errors, UniValue& result); /** * Parse a prevtxs UniValue array and get the map of coins from it diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp index d9ad70fa37..a7866474e1 100644 --- a/src/rpc/request.cpp +++ b/src/rpc/request.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/request.h b/src/rpc/request.h index 4761e9e371..de3a4ae840 100644 --- a/src/rpc/request.h +++ b/src/rpc/request.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -40,7 +40,7 @@ public: std::string peerAddr; const util::Ref& context; - JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {} + explicit JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {} //! Initializes request information from another request object and the //! given context. The implementation should be updated if any members are diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index f32d9abac6..9a9b3713f3 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -144,8 +144,13 @@ static RPCHelpMan help() [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue { std::string strCommand; - if (jsonRequest.params.size() > 0) + if (jsonRequest.params.size() > 0) { strCommand = jsonRequest.params[0].get_str(); + } + if (strCommand == "dump_all_command_conversions") { + // Used for testing only, undocumented + return tableRPC.dumpArgMap(); + } return tableRPC.help(strCommand, jsonRequest); }, @@ -244,13 +249,13 @@ static RPCHelpMan getrpcinfo() // clang-format off static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- +{ // category actor (function) + // --------------------- ----------------------- /* Overall control/query calls */ - { "control", "getrpcinfo", &getrpcinfo, {} }, - { "control", "help", &help, {"command"} }, - { "control", "stop", &stop, {"wait"} }, - { "control", "uptime", &uptime, {} }, + { "control", &getrpcinfo, }, + { "control", &help, }, + { "control", &stop, }, + { "control", &uptime, }, }; // clang-format on @@ -479,6 +484,18 @@ std::vector<std::string> CRPCTable::listCommands() const return commandList; } +UniValue CRPCTable::dumpArgMap() const +{ + UniValue ret{UniValue::VARR}; + for (const auto& cmd : mapCommands) { + for (const auto& c : cmd.second) { + const auto help = RpcMethodFnType(c->unique_id)(); + help.AppendArgMap(ret); + } + } + return ret; +} + void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { if (!timerInterface) diff --git a/src/rpc/server.h b/src/rpc/server.h index 7d13edb8b0..fe5a791e1e 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -103,7 +103,7 @@ public: } //! Simplified constructor taking plain RpcMethodFnType function pointer. - CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector<std::string> args_in) + CRPCCommand(std::string category, RpcMethodFnType fn) : CRPCCommand( category, fn().m_name, @@ -111,8 +111,6 @@ public: fn().GetArgNames(), intptr_t(fn)) { - CHECK_NONFATAL(fn().m_name == name_in); - CHECK_NONFATAL(fn().GetArgNames() == args_in); } std::string category; @@ -147,6 +145,10 @@ public: */ std::vector<std::string> listCommands() const; + /** + * Return all named arguments that need to be converted by the client from string to another JSON type + */ + UniValue dumpArgMap() const; /** * Appends a CRPCCommand to the dispatch table. diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 1b21587b6d..bfdba5253c 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2020 The Bitcoin Core developers +// Copyright (c) 2017-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -209,7 +209,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto return dest; } -class DescribeAddressVisitor : public boost::static_visitor<UniValue> +class DescribeAddressVisitor { public: explicit DescribeAddressVisitor() {} @@ -267,7 +267,7 @@ public: UniValue DescribeAddress(const CTxDestination& dest) { - return boost::apply_visitor(DescribeAddressVisitor(), dest); + return std::visit(DescribeAddressVisitor(), dest); } unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target) @@ -549,6 +549,24 @@ std::string RPCHelpMan::ToString() const return ret; } +void RPCHelpMan::AppendArgMap(UniValue& arr) const +{ + for (int i{0}; i < int(m_args.size()); ++i) { + const auto& arg = m_args.at(i); + std::vector<std::string> arg_names; + boost::split(arg_names, arg.m_names, boost::is_any_of("|")); + for (const auto& arg_name : arg_names) { + UniValue map{UniValue::VARR}; + map.push_back(m_name); + map.push_back(i); + map.push_back(arg_name); + map.push_back(arg.m_type == RPCArg::Type::STR || + arg.m_type == RPCArg::Type::STR_HEX); + arr.push_back(map); + } + } +} + std::string RPCArg::GetFirstName() const { return m_names.substr(0, m_names.find("|")); @@ -562,10 +580,10 @@ std::string RPCArg::GetName() const bool RPCArg::IsOptional() const { - if (m_fallback.which() == 1) { + if (m_fallback.index() == 1) { return true; } else { - return RPCArg::Optional::NO != boost::get<RPCArg::Optional>(m_fallback); + return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback); } } @@ -609,10 +627,10 @@ std::string RPCArg::ToDescriptionString() const } } // no default case, so the compiler can warn about missing cases } - if (m_fallback.which() == 1) { - ret += ", optional, default=" + boost::get<std::string>(m_fallback); + if (m_fallback.index() == 1) { + ret += ", optional, default=" + std::get<std::string>(m_fallback); } else { - switch (boost::get<RPCArg::Optional>(m_fallback)) { + switch (std::get<RPCArg::Optional>(m_fallback)) { case RPCArg::Optional::OMITTED: { // nothing to do. Element is treated as if not present and has no default value break; diff --git a/src/rpc/util.h b/src/rpc/util.h index 45b0bb0c7e..444a013ca1 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2020 The Bitcoin Core developers +// Copyright (c) 2017-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,10 +19,9 @@ #include <util/check.h> #include <string> +#include <variant> #include <vector> -#include <boost/variant.hpp> - /** * String used to describe UNIX epoch time in documentation, factored out to a * constant for consistency. @@ -144,7 +143,7 @@ struct RPCArg { */ OMITTED, }; - using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>; + using Fallback = std::variant<Optional, /* default value for optional args */ std::string>; const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments) const Type m_type; const bool m_hidden; @@ -337,6 +336,8 @@ public: RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun); std::string ToString() const; + /** Append the named args that need to be converted from string to another JSON type */ + void AppendArgMap(UniValue& arr) const; UniValue HandleRequest(const JSONRPCRequest& request) { Check(request); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 7c361bf26f..b3ee23f139 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -7,6 +7,7 @@ #include <random.h> #include <assert.h> +#include <functional> #include <utility> CScheduler::CScheduler() diff --git a/src/scheduler.h b/src/scheduler.h index d7fe00d1b4..9eec8c0fa0 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -9,6 +9,7 @@ #include <functional> #include <list> #include <map> +#include <thread> #include <sync.h> @@ -35,6 +36,8 @@ public: CScheduler(); ~CScheduler(); + std::thread m_service_thread; + typedef std::function<void()> Function; /** Call func at/after time t */ @@ -62,8 +65,7 @@ public: void MockForward(std::chrono::seconds delta_seconds); /** - * Services the queue 'forever'. Should be run in a thread, - * and interrupted using boost::interrupt_thread + * Services the queue 'forever'. Should be run in a thread. */ void serviceQueue(); @@ -72,12 +74,14 @@ public: { WITH_LOCK(newTaskMutex, stopRequested = true); newTaskScheduled.notify_all(); + if (m_service_thread.joinable()) m_service_thread.join(); } /** Tell any threads running serviceQueue to stop when there is no work left to be done */ void StopWhenDrained() { WITH_LOCK(newTaskMutex, stopWhenEmpty = true); newTaskScheduled.notify_all(); + if (m_service_thread.joinable()) m_service_thread.join(); } /** diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 15e204062f..76609f01a7 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -16,8 +16,7 @@ namespace { class TxInputStream { public: - TxInputStream(int nTypeIn, int nVersionIn, const unsigned char *txTo, size_t txToLen) : - m_type(nTypeIn), + TxInputStream(int nVersionIn, const unsigned char *txTo, size_t txToLen) : m_version(nVersionIn), m_data(txTo), m_remaining(txToLen) @@ -47,9 +46,7 @@ public: } int GetVersion() const { return m_version; } - int GetType() const { return m_type; } private: - const int m_type; const int m_version; const unsigned char* m_data; size_t m_remaining; @@ -84,7 +81,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP return set_error(err, bitcoinconsensus_ERR_INVALID_FLAGS); } try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + TxInputStream stream(PROTOCOL_VERSION, txTo, txToLen); CTransaction tx(deserialize, stream); if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 6c0a98cca2..9e4b8a9dd6 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -156,7 +156,7 @@ protected: uint32_t m_expr_index; public: - PubkeyProvider(uint32_t exp_index) : m_expr_index(exp_index) {} + explicit PubkeyProvider(uint32_t exp_index) : m_expr_index(exp_index) {} virtual ~PubkeyProvider() = default; @@ -565,7 +565,7 @@ public: Optional<OutputType> GetOutputType() const override { - switch (m_destination.which()) { + switch (m_destination.index()) { case 1 /* PKHash */: case 2 /* ScriptHash */: return OutputType::LEGACY; case 3 /* WitnessV0ScriptHash */: @@ -593,7 +593,7 @@ public: { CTxDestination dest; ExtractDestination(m_script, dest); - switch (dest.which()) { + switch (dest.index()) { case 1 /* PKHash */: case 2 /* ScriptHash */: return OutputType::LEGACY; case 3 /* WitnessV0ScriptHash */: @@ -731,7 +731,7 @@ enum class ParseScriptContext { }; /** Parse a key path, being passed a split list of elements (the first element is ignored). */ -NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, std::string& error) +[[nodiscard]] bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, std::string& error) { for (size_t i = 1; i < split.size(); ++i) { Span<const char> elem = split[i]; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 5735e7df66..ecac3b9e7e 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -305,8 +305,8 @@ private: uint32_t m_first_false_pos = NO_FALSE; public: - bool empty() { return m_stack_size == 0; } - bool all_true() { return m_first_false_pos == NO_FALSE; } + bool empty() const { return m_stack_size == 0; } + bool all_true() const { return m_first_false_pos == NO_FALSE; } void push_back(bool f) { if (m_first_false_pos == NO_FALSE && !f) { @@ -1834,9 +1834,13 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash) { const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE; + //! The inner pubkey (x-only, so no Y coordinate parity). const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))}; + //! The output pubkey (taken from the scriptPubKey). const XOnlyPubKey q{uint256(program)}; + // Compute the tapleaf hash. tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256(); + // Compute the Merkle root from the leaf and the provided path. uint256 k = tapleaf_hash; for (int i = 0; i < path_len; ++i) { CHashWriter ss_branch{HASHER_TAPBRANCH}; @@ -1848,7 +1852,9 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c } k = ss_branch.GetSHA256(); } + // Compute the tweak from the Merkle root and the inner pubkey. k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256(); + // Verify that the output pubkey matches the tweaked inner pubkey, after correcting for parity. return q.CheckPayToContract(p, k, control[0] & 1); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index c0c2b012c6..b4c163c841 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/keyorigin.h b/src/script/keyorigin.h index a318ff0f9d..210395d177 100644 --- a/src/script/keyorigin.h +++ b/src/script/keyorigin.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/script/script.cpp b/src/script/script.cpp index f31472e42d..9a6419088b 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script_error.h b/src/script/script_error.h index b071681613..44e68fe0fa 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index c1786140de..cf47d37e70 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,6 +11,8 @@ #include <util/system.h> #include <cuckoocache.h> + +#include <boost/thread/lock_types.hpp> #include <boost/thread/shared_mutex.hpp> namespace { @@ -66,7 +68,7 @@ public: return setValid.contains(entry, erase); } - void Set(uint256& entry) + void Set(const uint256& entry) { boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); setValid.insert(entry); diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 00534f9758..bf0ba38c2d 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,6 +8,7 @@ #include <script/interpreter.h> #include <span.h> +#include <util/hasher.h> #include <vector> @@ -20,27 +21,6 @@ static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384; class CPubKey; -/** - * We're hashing a nonce into the entries themselves, so we don't need extra - * blinding in the set hash computation. - * - * This may exhibit platform endian dependent behavior but because these are - * nonced hashes (random) and this state is only ever used locally it is safe. - * All that matters is local consistency. - */ -class SignatureCacheHasher -{ -public: - template <uint8_t hash_select> - uint32_t operator()(const uint256& key) const - { - static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); - uint32_t u; - std::memcpy(&u, key.begin()+4*hash_select, 4); - return u; - } -}; - class CachingTransactionSignatureChecker : public TransactionSignatureChecker { private: diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 0e6864d547..8afbe9ebed 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -388,7 +388,7 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; + const CTxIn& txin = txTo.vin[nIn]; assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index 2d8dc7d471..9781ec32af 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -179,18 +179,18 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& { // Only supports destinations which map to single public keys, i.e. P2PKH, // P2WPKH, and P2SH-P2WPKH. - if (auto id = boost::get<PKHash>(&dest)) { + if (auto id = std::get_if<PKHash>(&dest)) { return ToKeyID(*id); } - if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) { + if (auto witness_id = std::get_if<WitnessV0KeyHash>(&dest)) { return ToKeyID(*witness_id); } - if (auto script_hash = boost::get<ScriptHash>(&dest)) { + if (auto script_hash = std::get_if<ScriptHash>(&dest)) { CScript script; CScriptID script_id(*script_hash); CTxDestination inner_dest; if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) { - if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) { + if (auto inner_witness_id = std::get_if<WitnessV0KeyHash>(&inner_dest)) { return ToKeyID(*inner_witness_id); } } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index f2f81664f6..7967c01858 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -261,9 +261,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::v return true; } -namespace -{ -class CScriptVisitor : public boost::static_visitor<CScript> +namespace { +class CScriptVisitor { public: CScript operator()(const CNoDestination& dest) const @@ -300,7 +299,7 @@ public: CScript GetScriptForDestination(const CTxDestination& dest) { - return boost::apply_visitor(CScriptVisitor(), dest); + return std::visit(CScriptVisitor(), dest); } CScript GetScriptForRawPubKey(const CPubKey& pubKey) @@ -320,5 +319,5 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) } bool IsValidDestination(const CTxDestination& dest) { - return dest.which() != 0; + return dest.index() != 0; } diff --git a/src/script/standard.h b/src/script/standard.h index 721203385e..d5d87392ad 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -9,10 +9,8 @@ #include <script/interpreter.h> #include <uint256.h> -#include <boost/variant.hpp> - #include <string> - +#include <variant> static const bool DEFAULT_ACCEPT_DATACARRIER = true; @@ -28,7 +26,7 @@ protected: public: BaseHash() : m_hash() {} - BaseHash(const HashType& in) : m_hash(in) {} + explicit BaseHash(const HashType& in) : m_hash(in) {} unsigned char* begin() { @@ -211,7 +209,7 @@ struct WitnessUnknown * (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; +using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown>; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); diff --git a/src/shutdown.cpp b/src/shutdown.cpp index dec497d8ec..df5f996022 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -1,23 +1,112 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <shutdown.h> +#include <config/bitcoin-config.h> + +#include <assert.h> #include <atomic> +#ifdef WIN32 +#include <condition_variable> +#else +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#endif static std::atomic<bool> fRequestShutdown(false); +#ifdef WIN32 +/** On windows it is possible to simply use a condition variable. */ +std::mutex g_shutdown_mutex; +std::condition_variable g_shutdown_cv; +#else +/** On UNIX-like operating systems use the self-pipe trick. + * Index 0 will be the read end of the pipe, index 1 the write end. + */ +static int g_shutdown_pipe[2] = {-1, -1}; +#endif + +bool InitShutdownState() +{ +#ifndef WIN32 +#if HAVE_O_CLOEXEC + // If we can, make sure that the file descriptors are closed on exec() + // to prevent interference. + if (pipe2(g_shutdown_pipe, O_CLOEXEC) != 0) { + return false; + } +#else + if (pipe(g_shutdown_pipe) != 0) { + return false; + } +#endif +#endif + return true; +} void StartShutdown() { +#ifdef WIN32 + std::unique_lock<std::mutex> lk(g_shutdown_mutex); fRequestShutdown = true; + g_shutdown_cv.notify_one(); +#else + // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe. + // Make sure that the token is only written once even if multiple threads call this concurrently or in + // case of a reentrant signal. + if (!fRequestShutdown.exchange(true)) { + // Write an arbitrary byte to the write end of the shutdown pipe. + const char token = 'x'; + while (true) { + int result = write(g_shutdown_pipe[1], &token, 1); + if (result < 0) { + // Failure. It's possible that the write was interrupted by another signal. + // Other errors are unexpected here. + assert(errno == EINTR); + } else { + assert(result == 1); + break; + } + } + } +#endif } + void AbortShutdown() { + if (fRequestShutdown) { + // Cancel existing shutdown by waiting for it, this will reset condition flags and remove + // the shutdown token from the pipe. + WaitForShutdown(); + } fRequestShutdown = false; } + bool ShutdownRequested() { return fRequestShutdown; } + +void WaitForShutdown() +{ +#ifdef WIN32 + std::unique_lock<std::mutex> lk(g_shutdown_mutex); + g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); }); +#else + char token; + while (true) { + int result = read(g_shutdown_pipe[0], &token, 1); + if (result < 0) { + // Failure. Check if the read was interrupted by a signal. + // Other errors are unexpected here. + assert(errno == EINTR); + } else { + assert(result == 1); + break; + } + } +#endif +} diff --git a/src/shutdown.h b/src/shutdown.h index 3ed851c789..b2fbdb8cfb 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -1,13 +1,30 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SHUTDOWN_H #define BITCOIN_SHUTDOWN_H +/** Initialize shutdown state. This must be called before using either StartShutdown(), + * AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe. + */ +bool InitShutdownState(); + +/** Request shutdown of the application. */ void StartShutdown(); + +/** Clear shutdown flag. Only use this during init (before calling WaitForShutdown in any + * thread), or in the unit tests. Calling it in other circumstances will cause a race condition. + */ void AbortShutdown(); + +/** Returns true if a shutdown is requested, false otherwise. */ bool ShutdownRequested(); +/** Wait for StartShutdown to be called in any thread. This can only be used + * from a single thread. + */ +void WaitForShutdown(); + #endif diff --git a/src/span.h b/src/span.h index 4afb383a59..830164514b 100644 --- a/src/span.h +++ b/src/span.h @@ -18,6 +18,16 @@ #define ASSERT_IF_DEBUG(x) #endif +#if defined(__clang__) +#if __has_attribute(lifetimebound) +#define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] +#else +#define SPAN_ATTR_LIFETIMEBOUND +#endif +#else +#define SPAN_ATTR_LIFETIMEBOUND +#endif + /** A Span is an object that can refer to a contiguous sequence of objects. * * It implements a subset of C++20's std::span. @@ -84,6 +94,14 @@ class Span C* m_data; std::size_t m_size; + template <class T> + struct is_Span_int : public std::false_type {}; + template <class T> + struct is_Span_int<Span<T>> : public std::true_type {}; + template <class T> + struct is_Span : public is_Span_int<typename std::remove_cv<T>::type>{}; + + public: constexpr Span() noexcept : m_data(nullptr), m_size(0) {} @@ -134,8 +152,19 @@ public: * To prevent surprises, only Spans for constant value types are supported when passing in temporaries. * Note that this restriction does not exist when converting arrays or other Spans (see above). */ - template <typename V, typename std::enable_if<(std::is_const<C>::value || std::is_lvalue_reference<V>::value) && std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value && std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, int>::type = 0> - constexpr Span(V&& v) noexcept : m_data(v.data()), m_size(v.size()) {} + template <typename V> + constexpr Span(V& other SPAN_ATTR_LIFETIMEBOUND, + typename std::enable_if<!is_Span<V>::value && + std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value && + std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr) + : m_data(other.data()), m_size(other.size()){} + + template <typename V> + constexpr Span(const V& other SPAN_ATTR_LIFETIMEBOUND, + typename std::enable_if<!is_Span<V>::value && + std::is_convertible<typename std::remove_pointer<decltype(std::declval<const V&>().data())>::type (*)[], C (*)[]>::value && + std::is_convertible<decltype(std::declval<const V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr) + : m_data(other.data()), m_size(other.size()){} constexpr C* data() const noexcept { return m_data; } constexpr C* begin() const noexcept { return m_data; } @@ -192,9 +221,9 @@ public: /** MakeSpan for arrays: */ template <typename A, int N> Span<A> constexpr MakeSpan(A (&a)[N]) { return Span<A>(a, N); } /** MakeSpan for temporaries / rvalue references, only supporting const output. */ -template <typename V> constexpr auto MakeSpan(V&& v) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); } +template <typename V> constexpr auto MakeSpan(V&& v SPAN_ATTR_LIFETIMEBOUND) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); } /** MakeSpan for (lvalue) references, supporting mutable output. */ -template <typename V> constexpr auto MakeSpan(V& v) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; } +template <typename V> constexpr auto MakeSpan(V& v SPAN_ATTR_LIFETIMEBOUND) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; } /** Pop the last element off a span, and return a reference to that element. */ template <typename T> diff --git a/src/streams.h b/src/streams.h index a6e781b95b..e78da31cbc 100644 --- a/src/streams.h +++ b/src/streams.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 26de780f29..6965f40253 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -65,7 +65,7 @@ void* Arena::alloc(size_t size) // Pick a large enough free-chunk. Returns an iterator pointing to the first element that is not less than key. // This allocation strategy is best-fit. According to "Dynamic Storage Allocation: A Survey and Critical Review", - // Wilson et. al. 1995, http://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit + // Wilson et. al. 1995, https://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit // policies seem to work well in practice. auto size_ptr_it = size_to_free_chunk.lower_bound(size); if (size_ptr_it == size_to_free_chunk.end()) diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index b9e2e99d1a..03e4e371a3 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/sync.cpp b/src/sync.cpp index 322198a852..a2b62c2286 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -14,9 +14,11 @@ #include <util/threadnames.h> #include <map> +#include <mutex> #include <set> #include <system_error> #include <thread> +#include <type_traits> #include <unordered_map> #include <utility> #include <vector> @@ -135,16 +137,52 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac throw std::logic_error(strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b)); } -static void push_lock(void* c, const CLockLocation& locklocation) +static void double_lock_detected(const void* mutex, const LockStack& lock_stack) { + LogPrintf("DOUBLE LOCK DETECTED\n"); + LogPrintf("Lock order:\n"); + for (const LockStackItem& i : lock_stack) { + if (i.first == mutex) { + LogPrintf(" (*)"); /* Continued */ + } + LogPrintf(" %s\n", i.second.ToString()); + } + if (g_debug_lockorder_abort) { + tfm::format(std::cerr, + "Assertion failed: detected double lock for %s, details in debug log.\n", + lock_stack.back().second.ToString()); + abort(); + } + throw std::logic_error("double lock detected"); +} + +template <typename MutexType> +static void push_lock(MutexType* c, const CLockLocation& locklocation) +{ + constexpr bool is_recursive_mutex = + std::is_base_of<RecursiveMutex, MutexType>::value || + std::is_base_of<std::recursive_mutex, MutexType>::value; + LockData& lockdata = GetLockData(); std::lock_guard<std::mutex> lock(lockdata.dd_mutex); LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; lock_stack.emplace_back(c, locklocation); - for (const LockStackItem& i : lock_stack) { - if (i.first == c) - break; + for (size_t j = 0; j < lock_stack.size() - 1; ++j) { + const LockStackItem& i = lock_stack[j]; + if (i.first == c) { + if (is_recursive_mutex) { + break; + } + // It is not a recursive mutex and it appears in the stack two times: + // at position `j` and at the end (which we added just before this loop). + // Can't allow locking the same (non-recursive) mutex two times from the + // same thread as that results in an undefined behavior. + auto lock_stack_copy = lock_stack; + lock_stack.pop_back(); + double_lock_detected(c, lock_stack_copy); + // double_lock_detected() does not return. + } const LockPair p1 = std::make_pair(i.first, c); if (lockdata.lockorders.count(p1)) @@ -175,27 +213,40 @@ static void pop_lock() } } -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) +template <typename MutexType> +void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry) { push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName())); } +template void EnterCritical(const char*, const char*, int, Mutex*, bool); +template void EnterCritical(const char*, const char*, int, RecursiveMutex*, bool); +template void EnterCritical(const char*, const char*, int, std::mutex*, bool); +template void EnterCritical(const char*, const char*, int, std::recursive_mutex*, bool); void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) { - { - LockData& lockdata = GetLockData(); - std::lock_guard<std::mutex> lock(lockdata.dd_mutex); - - const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; - if (!lock_stack.empty()) { - const auto& lastlock = lock_stack.back(); - if (lastlock.first == cs) { - lockname = lastlock.second.Name(); - return; - } + LockData& lockdata = GetLockData(); + std::lock_guard<std::mutex> lock(lockdata.dd_mutex); + + const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()]; + if (!lock_stack.empty()) { + const auto& lastlock = lock_stack.back(); + if (lastlock.first == cs) { + lockname = lastlock.second.Name(); + return; } } - throw std::system_error(EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname)); + + LogPrintf("INCONSISTENT LOCK ORDER DETECTED\n"); + LogPrintf("Current lock order (least recent first) is:\n"); + for (const LockStackItem& i : lock_stack) { + LogPrintf(" %s\n", i.second.ToString()); + } + if (g_debug_lockorder_abort) { + tfm::format(std::cerr, "%s:%s %s was not most recent critical section locked, details in debug log.\n", file, line, guardname); + abort(); + } + throw std::logic_error(strprintf("%s was not most recent critical section locked", guardname)); } void LeaveCritical() diff --git a/src/sync.h b/src/sync.h index 41f4e43bdd..53213c2089 100644 --- a/src/sync.h +++ b/src/sync.h @@ -48,7 +48,8 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII /////////////////////////////// #ifdef DEBUG_LOCKORDER -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); +template <typename MutexType> +void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false); void LeaveCritical(); void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line); std::string LocksHeld(); @@ -66,7 +67,8 @@ bool LockStackEmpty(); */ extern bool g_debug_lockorder_abort; #else -inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} +template <typename MutexType> +inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {} inline void LeaveCritical() {} inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {} template <typename MutexType> @@ -135,7 +137,7 @@ class SCOPED_LOCKABLE UniqueLock : public Base private: void Enter(const char* pszName, const char* pszFile, int nLine) { - EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex())); + EnterCritical(pszName, pszFile, nLine, Base::mutex()); #ifdef DEBUG_LOCKCONTENTION if (!Base::try_lock()) { PrintLockContention(pszName, pszFile, nLine); @@ -148,7 +150,7 @@ private: bool TryEnter(const char* pszName, const char* pszFile, int nLine) { - EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true); + EnterCritical(pszName, pszFile, nLine, Base::mutex(), true); Base::try_lock(); if (!Base::owns_lock()) LeaveCritical(); @@ -205,7 +207,7 @@ public: ~reverse_lock() { templock.swap(lock); - EnterCritical(lockname.c_str(), file.c_str(), line, (void*)lock.mutex()); + EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex()); lock.lock(); } @@ -236,14 +238,16 @@ using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove #define ENTER_CRITICAL_SECTION(cs) \ { \ - EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ + EnterCritical(#cs, __FILE__, __LINE__, &cs); \ (cs).lock(); \ } -#define LEAVE_CRITICAL_SECTION(cs) \ - { \ - (cs).unlock(); \ - LeaveCritical(); \ +#define LEAVE_CRITICAL_SECTION(cs) \ + { \ + std::string lockname; \ + CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \ + (cs).unlock(); \ + LeaveCritical(); \ } //! Run code while locking a mutex. @@ -254,7 +258,22 @@ using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove //! //! int val = WITH_LOCK(cs, return shared_val); //! -#define WITH_LOCK(cs, code) [&] { LOCK(cs); code; }() +//! Note: +//! +//! Since the return type deduction follows that of decltype(auto), while the +//! deduced type of: +//! +//! WITH_LOCK(cs, return {int i = 1; return i;}); +//! +//! is int, the deduced type of: +//! +//! WITH_LOCK(cs, return {int j = 1; return (j);}); +//! +//! is &int, a reference to a local variable +//! +//! The above is detectable at compile-time with the -Wreturn-local-addr flag in +//! gcc and the -Wreturn-stack-address flag in clang, both enabled by default. +#define WITH_LOCK(cs, code) [&]() -> decltype(auto) { LOCK(cs); code; }() class CSemaphore { diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 25fdd64568..37ff8a9afe 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -71,7 +71,7 @@ public: } // Simulates connection failure so that we can test eviction of offline nodes - void SimConnFail(CService& addr) + void SimConnFail(const CService& addr) { LOCK(cs); int64_t nLastSuccess = 1; diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp index c16519a6b1..1a39498899 100644 --- a/src/test/amount_tests.cpp +++ b/src/test/amount_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index e55d6b3b19..dd760fe999 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 14cf1a4a76..9dfbd7ba7c 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index 00c4bdc14e..633a95ce96 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) const CBlockIndex* block_index; { LOCK(cs_main); - block_index = LookupBlockIndex(block->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); @@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) const CBlockIndex* block_index; { LOCK(cs_main); - block_index = LookupBlockIndex(block->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); @@ -210,7 +210,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) const CBlockIndex* block_index; { LOCK(cs_main); - block_index = LookupBlockIndex(block->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); @@ -231,14 +231,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) { LOCK(cs_main); - block_index = LookupBlockIndex(chainA[i]->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); CheckFilterLookups(filter_index, block_index, chainA_last_header); { LOCK(cs_main); - block_index = LookupBlockIndex(chainB[i]->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); CheckFilterLookups(filter_index, block_index, chainB_last_header); diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp index 178c261365..8eb4dbc592 100644 --- a/src/test/blockfilter_tests.cpp +++ b/src/test/blockfilter_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 8348810ac1..21921375b3 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -10,7 +10,6 @@ #include <util/time.h> #include <boost/test/unit_test.hpp> -#include <boost/thread/thread.hpp> #include <atomic> #include <condition_variable> @@ -26,7 +25,7 @@ static const unsigned int QUEUE_BATCH_SIZE = 128; static const int SCRIPT_CHECK_THREADS = 3; struct FakeCheck { - bool operator()() + bool operator()() const { return true; } @@ -47,7 +46,7 @@ struct FailingCheck { bool fails; FailingCheck(bool _fails) : fails(_fails){}; FailingCheck() : fails(true){}; - bool operator()() + bool operator()() const { return !fails; } @@ -76,7 +75,7 @@ struct UniqueCheck { struct MemoryCheck { static std::atomic<size_t> fake_allocated_memory; bool b {false}; - bool operator()() + bool operator()() const { return true; } @@ -107,7 +106,7 @@ struct FrozenCleanupCheck { // Freezing can't be the default initialized behavior given how the queue // swaps in default initialized Checks. bool should_freeze {false}; - bool operator()() + bool operator()() const { return true; } @@ -148,10 +147,7 @@ typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue; static void Correct_Queue_range(std::vector<size_t> range) { auto small_queue = MakeUnique<Correct_Queue>(QUEUE_BATCH_SIZE); - boost::thread_group tg; - for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) { - tg.create_thread([&]{small_queue->Thread();}); - } + small_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); // Make vChecks here to save on malloc (this test can be slow...) std::vector<FakeCheckCheckCompletion> vChecks; for (const size_t i : range) { @@ -168,8 +164,7 @@ static void Correct_Queue_range(std::vector<size_t> range) BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i); } } - tg.interrupt_all(); - tg.join_all(); + small_queue->StopWorkerThreads(); } /** Test that 0 checks is correct @@ -212,11 +207,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) { auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE); - - boost::thread_group tg; - for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) { - tg.create_thread([&]{fail_queue->Thread();}); - } + fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (size_t i = 0; i < 1001; ++i) { CCheckQueueControl<FailingCheck> control(fail_queue.get()); @@ -237,18 +228,14 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) BOOST_REQUIRE(success); } } - tg.interrupt_all(); - tg.join_all(); + fail_queue->StopWorkerThreads(); } // Test that a block validation which fails does not interfere with // future blocks, ie, the bad state is cleared. BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) { auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE); - boost::thread_group tg; - for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) { - tg.create_thread([&]{fail_queue->Thread();}); - } + fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (auto times = 0; times < 10; ++times) { for (const bool end_fails : {true, false}) { @@ -263,8 +250,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) BOOST_REQUIRE(r != end_fails); } } - tg.interrupt_all(); - tg.join_all(); + fail_queue->StopWorkerThreads(); } // Test that unique checks are actually all called individually, rather than @@ -273,11 +259,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) { auto queue = MakeUnique<Unique_Queue>(QUEUE_BATCH_SIZE); - boost::thread_group tg; - for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) { - tg.create_thread([&]{queue->Thread();}); - - } + queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); size_t COUNT = 100000; size_t total = COUNT; @@ -300,8 +282,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) } BOOST_REQUIRE(r); } - tg.interrupt_all(); - tg.join_all(); + queue->StopWorkerThreads(); } @@ -313,10 +294,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) { auto queue = MakeUnique<Memory_Queue>(QUEUE_BATCH_SIZE); - boost::thread_group tg; - for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) { - tg.create_thread([&]{queue->Thread();}); - } + queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (size_t i = 0; i < 1000; ++i) { size_t total = i; { @@ -335,8 +313,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) } BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0U); } - tg.interrupt_all(); - tg.join_all(); + queue->StopWorkerThreads(); } // Test that a new verification cannot occur until all checks @@ -344,11 +321,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) { auto queue = MakeUnique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE); - boost::thread_group tg; bool fails = false; - for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) { - tg.create_thread([&]{queue->Thread();}); - } + queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); std::thread t0([&]() { CCheckQueueControl<FrozenCleanupCheck> control(queue.get()); std::vector<FrozenCleanupCheck> vChecks(1); @@ -367,7 +341,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) } // Try to get control of the queue a bunch of times for (auto x = 0; x < 100 && !fails; ++x) { - fails = queue->ControlMutex.try_lock(); + fails = queue->m_control_mutex.try_lock(); } { // Unfreeze (we need lock n case of spurious wakeup) @@ -378,9 +352,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) FrozenCleanupCheck::cv.notify_one(); // Wait for control to finish t0.join(); - tg.interrupt_all(); - tg.join_all(); BOOST_REQUIRE(!fails); + queue->StopWorkerThreads(); } @@ -389,11 +362,11 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) { auto queue = MakeUnique<Standard_Queue>(QUEUE_BATCH_SIZE); { - boost::thread_group tg; + std::vector<std::thread> tg; std::atomic<int> nThreads {0}; std::atomic<int> fails {0}; for (size_t i = 0; i < 3; ++i) { - tg.create_thread( + tg.emplace_back( [&]{ CCheckQueueControl<FakeCheck> control(queue.get()); // While sleeping, no other thread should execute to this point @@ -402,11 +375,13 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) fails += observed != nThreads; }); } - tg.join_all(); + for (auto& thread: tg) { + if (thread.joinable()) thread.join(); + } BOOST_REQUIRE_EQUAL(fails, 0); } { - boost::thread_group tg; + std::vector<std::thread> tg; std::mutex m; std::condition_variable cv; bool has_lock{false}; @@ -415,7 +390,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) bool done_ack{false}; { std::unique_lock<std::mutex> l(m); - tg.create_thread([&]{ + tg.emplace_back([&]{ CCheckQueueControl<FakeCheck> control(queue.get()); std::unique_lock<std::mutex> ll(m); has_lock = true; @@ -431,7 +406,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) cv.wait(l, [&](){return has_lock;}); bool fails = false; for (auto x = 0; x < 100 && !fails; ++x) { - fails = queue->ControlMutex.try_lock(); + fails = queue->m_control_mutex.try_lock(); } has_tried = true; cv.notify_one(); @@ -441,8 +416,9 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) cv.notify_one(); BOOST_REQUIRE(!fails); } - tg.join_all(); + for (auto& thread: tg) { + if (thread.joinable()) thread.join(); + } } } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 173ec5e3d9..06db3b846e 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 The Bitcoin Core developers +// Copyright (c) 2014-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -38,7 +38,7 @@ class CCoinsViewTest : public CCoinsView std::map<COutPoint, Coin> map_; public: - NODISCARD bool GetCoin(const COutPoint& outpoint, Coin& coin) const override + [[nodiscard]] bool GetCoin(const COutPoint& outpoint, Coin& coin) const override { std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint); if (it == map_.end()) { diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 0ad5066603..7358b246b6 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -14,7 +14,9 @@ #include <crypto/sha256.h> #include <crypto/sha3.h> #include <crypto/sha512.h> +#include <crypto/muhash.h> #include <random.h> +#include <streams.h> #include <test/util/setup_common.h> #include <util/strencodings.h> @@ -857,4 +859,92 @@ BOOST_AUTO_TEST_CASE(sha3_256_tests) TestSHA3_256("72c57c359e10684d0517e46653a02d18d29eff803eb009e4d5eb9e95add9ad1a4ac1f38a70296f3a369a16985ca3c957de2084cdc9bdd8994eb59b8815e0debad4ec1f001feac089820db8becdaf896aaf95721e8674e5d476b43bd2b873a7d135cd685f545b438210f9319e4dcd55986c85303c1ddf18dc746fe63a409df0a998ed376eb683e16c09e6e9018504152b3e7628ef350659fb716e058a5263a18823d2f2f6ee6a8091945a48ae1c5cb1694cf2c1fe76ef9177953afe8899cfa2b7fe0603bfa3180937dadfb66fbbdd119bbf8063338aa4a699075a3bfdbae8db7e5211d0917e9665a702fc9b0a0a901d08bea97654162d82a9f05622b060b634244779c33427eb7a29353a5f48b07cbefa72f3622ac5900bef77b71d6b314296f304c8426f451f32049b1f6af156a9dab702e8907d3cd72bb2c50493f4d593e731b285b70c803b74825b3524cda3205a8897106615260ac93c01c5ec14f5b11127783989d1824527e99e04f6a340e827b559f24db9292fcdd354838f9339a5fa1d7f6b2087f04835828b13463dd40927866f16ae33ed501ec0e6c4e63948768c5aeea3e4f6754985954bea7d61088c44430204ef491b74a64bde1358cecb2cad28ee6a3de5b752ff6a051104d88478653339457ac45ba44cbb65f54d1969d047cda746931d5e6a8b48e211416aefd5729f3d60b56b54e7f85aa2f42de3cb69419240c24e67139a11790a709edef2ac52cf35dd0a08af45926ebe9761f498ff83bfe263d6897ee97943a4b982fe3404ef0b4a45e06113c60340e0664f14799bf59cb4b3934b465fabefd87155905ee5309ba41e9e402973311831ea600b16437f71df39ee77130490c4d0227e5d1757fdc66af3ae6b9953053ed9aafca0160209858a7d4dd38fe10e0cb153672d08633ed6c54977aa0a6e67f9ff2f8c9d22dd7b21de08192960fd0e0da68d77c8d810db11dcaa61c725cd4092cbff76c8e1debd8d0361bb3f2e607911d45716f53067bdc0d89dd4889177765166a424e9fc0cb711201099dda213355e6639ac7eb86eca2ae0ab38b7f674f37ef8a6fcca1a6f52f55d9e1dcd631d2c3c82bba129172feb991d5af51afecd9d61a88b6832e4107480e392aed61a8644f551665ebff6b20953b635737a4f895e429fddcfe801f606fbda74b3bf6f5767d0fac14907fcfd0aa1d4c11b9e91b01d68052399b51a29f1ae6acd965109977c14a555cbcbd21ad8cb9f8853506d4bc21c01e62d61d7b21be1b923be54914e6b0a7ca84dd11f1159193e1184568a6134a6bbadf5b4df986edcf2019390ae841cfaa44435e28ce877d3dae4177992fa5d4e5c005876dbe3d1e63bec7dcc0942762b48b1ecc6c1a918409a8a72812a1e245c0c67be6e729c2b49bc6ee4d24a8f63e78e75db45655c26a9a78aff36fcd67117f26b8f654dca664b9f0e30681874cb749e1a692720078856286c2560b0292cc837933423147569350955c9571bf8941ba128fd339cb4268f46b94bc6ee203eb7026813706ea51c4f24c91866fc23a724bf2501327e6ae89c29f8db315dc28d2c7c719514036367e018f4835f63fdecd71f9bdced7132b6c4f8b13c69a517026fcd3622d67cb632320d5e7308f78f4b7cea11f6291b137851dc6cd6366f2785c71c3f237f81a7658b2a8d512b61e0ad5a4710b7b124151689fcb2116063fbff7e9115fed7b93de834970b838e49f8f8ba5f1f874c354078b5810a55ae289a56da563f1da6cd80a3757d6073fa55e016e45ac6cec1f69d871c92fd0ae9670c74249045e6b464787f9504128736309fed205f8df4d90e332908581298d9c75a3fa36ab0c3c9272e62de53ab290c803d67b696fd615c260a47bffad16746f18ba1a10a061bacbea9369693b3c042eec36bed289d7d12e52bca8aa1c2dff88ca7816498d25626d0f1e106ebb0b4a12138e00f3df5b1c2f49d98b1756e69b641b7c6353d99dbff050f4d76842c6cf1c2a4b062fc8e6336fa689b7c9d5c6b4ab8c15a5c20e514ff070a602d85ae52fa7810c22f8eeffd34a095b93342144f7a98d024216b3d68ed7bea047517bfcd83ec83febd1ba0e5858e2bdc1d8b1f7b0f89e90ccc432a3f930cb8209462e64556c5054c56ca2a85f16b32eb83a10459d13516faa4d23302b7607b9bd38dab2239ac9e9440c314433fdfb3ceadab4b4f87415ed6f240e017221f3b5f7ac196cdf54957bec42fe6893994b46de3d27dc7fb58ca88feb5b9e79cf20053d12530ac524337b22a3629bea52f40b06d3e2128f32060f9105847daed81d35f20e2002817434659baff64494c5b5c7f9216bfda38412a0f70511159dc73bb6bae1f8eaa0ef08d99bcb31f94f6be12c29c83df45926430b366c99fca3270c15fc4056398fdf3135b7779e3066a006961d1ac0ad1c83179ce39e87a96b722ec23aabc065badf3e188347a360772ca6a447abac7e6a44f0d4632d52926332e44a0a86bff5ce699fd063bdda3ffd4c41b53ded49fecec67f40599b934e16e3fd1bc063ad7026f8d71bfd4cbaf56599586774723194b692036f1b6bb242e2ffb9c600b5215b412764599476ce475c9e5b396fbcebd6be323dcf4d0048077400aac7500db41dc95fc7f7edbe7c9c2ec5ea89943fe13b42217eef530bbd023671509e12dfce4e1c1c82955d965e6a68aa66f6967dba48feda572db1f099d9a6dc4bc8edade852b5e824a06890dc48a6a6510ecaf8cf7620d757290e3166d431abecc624fa9ac2234d2eb783308ead45544910c633a94964b2ef5fbc409cb8835ac4147d384e12e0a5e13951f7de0ee13eafcb0ca0c04946d7804040c0a3cd088352424b097adb7aad1ca4495952f3e6c0158c02d2bcec33bfda69301434a84d9027ce02c0b9725dad118", "d894b86261436362e64241e61f6b3e6589daf64dc641f60570c4c0bf3b1f2ca3"); } +static MuHash3072 FromInt(unsigned char i) { + unsigned char tmp[32] = {i, 0}; + return MuHash3072(tmp); +} + +BOOST_AUTO_TEST_CASE(muhash_tests) +{ + uint256 out; + + for (int iter = 0; iter < 10; ++iter) { + uint256 res; + int table[4]; + for (int i = 0; i < 4; ++i) { + table[i] = g_insecure_rand_ctx.randbits(3); + } + for (int order = 0; order < 4; ++order) { + MuHash3072 acc; + for (int i = 0; i < 4; ++i) { + int t = table[i ^ order]; + if (t & 4) { + acc /= FromInt(t & 3); + } else { + acc *= FromInt(t & 3); + } + } + acc.Finalize(out); + if (order == 0) { + res = out; + } else { + BOOST_CHECK(res == out); + } + } + + MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X + MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X, y=Y + MuHash3072 z; // x=X, y=Y, z=1 + z *= x; // x=X, y=Y, z=X + z *= y; // x=X, y=Y, z=X*Y + y *= x; // x=X, y=Y*X, z=X*Y + z /= y; // x=X, y=Y*X, z=1 + z.Finalize(out); + + uint256 out2; + MuHash3072 a; + a.Finalize(out2); + + BOOST_CHECK_EQUAL(out, out2); + } + + MuHash3072 acc = FromInt(0); + acc *= FromInt(1); + acc /= FromInt(2); + acc.Finalize(out); + BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863")); + + MuHash3072 acc2 = FromInt(0); + unsigned char tmp[32] = {1, 0}; + acc2.Insert(tmp); + unsigned char tmp2[32] = {2, 0}; + acc2.Remove(tmp2); + acc2.Finalize(out); + BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863")); + + // Test MuHash3072 serialization + MuHash3072 serchk = FromInt(1); serchk *= FromInt(2); + std::string ser_exp = "1fa093295ea30a6a3acdc7b3f770fa538eff537528e990e2910e40bbcfd7f6696b1256901929094694b56316de342f593303dd12ac43e06dce1be1ff8301c845beb15468fff0ef002dbf80c29f26e6452bccc91b5cb9437ad410d2a67ea847887fa3c6a6553309946880fe20db2c73fe0641adbd4e86edfee0d9f8cd0ee1230898873dc13ed8ddcaf045c80faa082774279007a2253f8922ee3ef361d378a6af3ddaf180b190ac97e556888c36b3d1fb1c85aab9ccd46e3deaeb7b7cf5db067a7e9ff86b658cf3acd6662bbcce37232daa753c48b794356c020090c831a8304416e2aa7ad633c0ddb2f11be1be316a81be7f7e472071c042cb68faef549c221ebff209273638b741aba5a81675c45a5fa92fea4ca821d7a324cb1e1a2ccd3b76c4228ec8066dad2a5df6e1bd0de45c7dd5de8070bdb46db6c554cf9aefc9b7b2bbf9f75b1864d9f95005314593905c0109b71f703d49944ae94477b51dac10a816bb6d1c700bafabc8bd86fac8df24be519a2f2836b16392e18036cb13e48c5c010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + CDataStream ss_chk(SER_DISK, PROTOCOL_VERSION); + ss_chk << serchk; + BOOST_CHECK_EQUAL(ser_exp, HexStr(ss_chk.str())); + + // Test MuHash3072 deserialization + MuHash3072 deserchk; + ss_chk >> deserchk; + uint256 out3; + serchk.Finalize(out); + deserchk.Finalize(out3); + BOOST_CHECK_EQUAL(HexStr(out), HexStr(out3)); + + // Test MuHash3072 overflow, meaning the internal data is larger than the modulus. + CDataStream ss_max(ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), SER_DISK, PROTOCOL_VERSION); + MuHash3072 overflowchk; + ss_max >> overflowchk; + + uint256 out4; + overflowchk.Finalize(out4); + BOOST_CHECK_EQUAL(HexStr(out4), "3a31e6903aff0de9f62f9a9f7f8b861de76ce2cda09822b90014319ae5dc2271"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 3a951d28ae..75c7e47e64 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.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 <boost/test/unit_test.hpp> +#include <boost/thread/lock_types.hpp> +#include <boost/thread/shared_mutex.hpp> #include <cuckoocache.h> #include <deque> #include <random.h> diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index c399da900f..cf6009d591 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -80,11 +80,12 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) { const CChainParams& chainparams = Params(); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY); + CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY); dummyNode1.SetCommonVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(&dummyNode1); @@ -135,7 +136,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman) { CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE); - vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY)); + vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY)); CNode &node = *vNodes.back(); node.SetCommonVersion(PROTOCOL_VERSION); @@ -149,7 +150,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) { const CChainParams& chainparams = Params(); auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = PeerManager::make(chainparams, *connman, nullptr, *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS; CConnman::Options options; @@ -222,11 +224,12 @@ 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<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); banman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND); + CNode dummyNode1(id++, NODE_NETWORK, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND); dummyNode1.SetCommonVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(&dummyNode1); dummyNode1.fSuccessfullyConnected = true; @@ -239,7 +242,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not discouraged CAddress addr2(ip(0xa0b0c002), NODE_NONE); - CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND); + CNode dummyNode2(id++, NODE_NETWORK, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND); dummyNode2.SetCommonVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(&dummyNode2); dummyNode2.fSuccessfullyConnected = true; @@ -268,14 +271,15 @@ 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<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); + auto peerLogic = PeerManager::make(chainparams, *connman, banman.get(), *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); 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(), "", ConnectionType::INBOUND); + CNode dummyNode(id++, NODE_NETWORK, INVALID_SOCKET, addr, 4, 4, CAddress(), "", ConnectionType::INBOUND); dummyNode.SetCommonVersion(PROTOCOL_VERSION); peerLogic->InitializeNode(&dummyNode); dummyNode.fSuccessfullyConnected = true; diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp index be7484cd0b..0c5c19113d 100644 --- a/src/test/flatfile_tests.cpp +++ b/src/test/flatfile_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp index d02c3613ba..ec487aa3ff 100644 --- a/src/test/fs_tests.cpp +++ b/src/test/fs_tests.cpp @@ -52,6 +52,23 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream) file >> input_buffer; BOOST_CHECK_EQUAL(input_buffer, "bitcoin"); } + { + // Join an absolute path and a relative path. + fs::path p = fsbridge::AbsPathJoin(tmpfolder, "fs_tests_₿_🏃"); + BOOST_CHECK(p.is_absolute()); + BOOST_CHECK_EQUAL(tmpfile1, p); + } + { + // Join two absolute paths. + fs::path p = fsbridge::AbsPathJoin(tmpfile1, tmpfile2); + BOOST_CHECK(p.is_absolute()); + BOOST_CHECK_EQUAL(tmpfile2, p); + } + { + // Ensure joining with empty paths does not add trailing path components. + BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, "")); + BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, {})); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/FuzzedDataProvider.h b/src/test/fuzz/FuzzedDataProvider.h index 3e069eba69..744a9d78ce 100644 --- a/src/test/fuzz/FuzzedDataProvider.h +++ b/src/test/fuzz/FuzzedDataProvider.h @@ -14,6 +14,7 @@ #define LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ #include <algorithm> +#include <array> #include <climits> #include <cstddef> #include <cstdint> @@ -34,272 +35,362 @@ class FuzzedDataProvider { : data_ptr_(data), remaining_bytes_(size) {} ~FuzzedDataProvider() = default; - // Returns a std::vector containing |num_bytes| of input data. If fewer than - // |num_bytes| of data remain, returns a shorter std::vector containing all - // of the data that's left. Can be used with any byte sized type, such as - // char, unsigned char, uint8_t, etc. - template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes) { - num_bytes = std::min(num_bytes, remaining_bytes_); - return ConsumeBytes<T>(num_bytes, num_bytes); - } + // See the implementation below (after the class definition) for more verbose + // comments for each of the methods. - // Similar to |ConsumeBytes|, but also appends the terminator value at the end - // of the resulting vector. Useful, when a mutable null-terminated C-string is - // needed, for example. But that is a rare case. Better avoid it, if possible, - // and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods. + // Methods returning std::vector of bytes. These are the most popular choice + // when splitting fuzzing input into pieces, as every piece is put into a + // separate buffer (i.e. ASan would catch any under-/overflow) and the memory + // will be released automatically. + template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes); template <typename T> - std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, - T terminator = 0) { - num_bytes = std::min(num_bytes, remaining_bytes_); - std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes); - result.back() = terminator; - return result; - } + std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, T terminator = 0); + template <typename T> std::vector<T> ConsumeRemainingBytes(); - // Returns a std::string containing |num_bytes| of input data. Using this and - // |.c_str()| on the resulting string is the best way to get an immutable - // null-terminated C string. If fewer than |num_bytes| of data remain, returns - // a shorter std::string containing all of the data that's left. - std::string ConsumeBytesAsString(size_t num_bytes) { - static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), - "ConsumeBytesAsString cannot convert the data to a string."); - - num_bytes = std::min(num_bytes, remaining_bytes_); - std::string result( - reinterpret_cast<const std::string::value_type *>(data_ptr_), - num_bytes); - Advance(num_bytes); - return result; - } + // Methods returning strings. Use only when you need a std::string or a null + // terminated C-string. Otherwise, prefer the methods returning std::vector. + std::string ConsumeBytesAsString(size_t num_bytes); + std::string ConsumeRandomLengthString(size_t max_length); + std::string ConsumeRandomLengthString(); + std::string ConsumeRemainingBytesAsString(); - // Returns a number in the range [min, max] by consuming bytes from the - // input data. The value might not be uniformly distributed in the given - // range. If there's no input data left, always returns |min|. |min| must - // be less than or equal to |max|. - template <typename T> T ConsumeIntegralInRange(T min, T max) { - static_assert(std::is_integral<T>::value, "An integral type is required."); - static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type."); + // Methods returning integer values. + template <typename T> T ConsumeIntegral(); + template <typename T> T ConsumeIntegralInRange(T min, T max); - if (min > max) - abort(); + // Methods returning floating point values. + template <typename T> T ConsumeFloatingPoint(); + template <typename T> T ConsumeFloatingPointInRange(T min, T max); - // Use the biggest type possible to hold the range and the result. - uint64_t range = static_cast<uint64_t>(max) - min; - uint64_t result = 0; - size_t offset = 0; - - while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && - remaining_bytes_ != 0) { - // Pull bytes off the end of the seed data. Experimentally, this seems to - // allow the fuzzer to more easily explore the input space. This makes - // sense, since it works by modifying inputs that caused new code to run, - // and this data is often used to encode length of data read by - // |ConsumeBytes|. Separating out read lengths makes it easier modify the - // contents of the data that is actually read. - --remaining_bytes_; - result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; - offset += CHAR_BIT; - } + // 0 <= return value <= 1. + template <typename T> T ConsumeProbability(); - // Avoid division by 0, in case |range + 1| results in overflow. - if (range != std::numeric_limits<decltype(range)>::max()) - result = result % (range + 1); + bool ConsumeBool(); - return static_cast<T>(min + result); - } + // Returns a value chosen from the given enum. + template <typename T> T ConsumeEnum(); - // Returns a std::string of length from 0 to |max_length|. When it runs out of - // input data, returns what remains of the input. Designed to be more stable - // with respect to a fuzzer inserting characters than just picking a random - // length and then consuming that many bytes with |ConsumeBytes|. - std::string ConsumeRandomLengthString(size_t max_length) { - // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" - // followed by anything else to the end of the string. As a result of this - // logic, a fuzzer can insert characters into the string, and the string - // will be lengthened to include those new characters, resulting in a more - // stable fuzzer than picking the length of a string independently from - // picking its contents. - std::string result; - - // Reserve the anticipated capaticity to prevent several reallocations. - result.reserve(std::min(max_length, remaining_bytes_)); - for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { - char next = ConvertUnsignedToSigned<char>(data_ptr_[0]); - Advance(1); - if (next == '\\' && remaining_bytes_ != 0) { - next = ConvertUnsignedToSigned<char>(data_ptr_[0]); - Advance(1); - if (next != '\\') - break; - } - result += next; - } - - result.shrink_to_fit(); - return result; - } + // Returns a value from the given array. + template <typename T, size_t size> T PickValueInArray(const T (&array)[size]); + template <typename T, size_t size> + T PickValueInArray(const std::array<T, size> &array); + template <typename T> T PickValueInArray(std::initializer_list<const T> list); - // Returns a std::vector containing all remaining bytes of the input data. - template <typename T> std::vector<T> ConsumeRemainingBytes() { - return ConsumeBytes<T>(remaining_bytes_); - } + // Writes data to the given destination and returns number of bytes written. + size_t ConsumeData(void *destination, size_t num_bytes); - // Returns a std::string containing all remaining bytes of the input data. - // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string - // object. - std::string ConsumeRemainingBytesAsString() { - return ConsumeBytesAsString(remaining_bytes_); - } + // Reports the remaining bytes available for fuzzed input. + size_t remaining_bytes() { return remaining_bytes_; } - // Returns a number in the range [Type's min, Type's max]. The value might - // not be uniformly distributed in the given range. If there's no input data - // left, always returns |min|. - template <typename T> T ConsumeIntegral() { - return ConsumeIntegralInRange(std::numeric_limits<T>::min(), - std::numeric_limits<T>::max()); - } + private: + FuzzedDataProvider(const FuzzedDataProvider &) = delete; + FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; - // Reads one byte and returns a bool, or false when no data remains. - bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); } + void CopyAndAdvance(void *destination, size_t num_bytes); - // Returns a copy of the value selected from the given fixed-size |array|. - template <typename T, size_t size> - T PickValueInArray(const T (&array)[size]) { - static_assert(size > 0, "The array must be non empty."); - return array[ConsumeIntegralInRange<size_t>(0, size - 1)]; - } + void Advance(size_t num_bytes); template <typename T> - T PickValueInArray(std::initializer_list<const T> list) { - // TODO(Dor1s): switch to static_assert once C++14 is allowed. - if (!list.size()) - abort(); - - return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1)); - } - - // Returns an enum value. The enum must start at 0 and be contiguous. It must - // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: - // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; - template <typename T> T ConsumeEnum() { - static_assert(std::is_enum<T>::value, "|T| must be an enum type."); - return static_cast<T>(ConsumeIntegralInRange<uint32_t>( - 0, static_cast<uint32_t>(T::kMaxValue))); - } + std::vector<T> ConsumeBytes(size_t size, size_t num_bytes); - // Returns a floating point number in the range [0.0, 1.0]. If there's no - // input data left, always returns 0. - template <typename T> T ConsumeProbability() { - static_assert(std::is_floating_point<T>::value, - "A floating point type is required."); + template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value); - // Use different integral types for different floating point types in order - // to provide better density of the resulting values. - using IntegralType = - typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t, - uint64_t>::type; + const uint8_t *data_ptr_; + size_t remaining_bytes_; +}; - T result = static_cast<T>(ConsumeIntegral<IntegralType>()); - result /= static_cast<T>(std::numeric_limits<IntegralType>::max()); - return result; +// Returns a std::vector containing |num_bytes| of input data. If fewer than +// |num_bytes| of data remain, returns a shorter std::vector containing all +// of the data that's left. Can be used with any byte sized type, such as +// char, unsigned char, uint8_t, etc. +template <typename T> +std::vector<T> FuzzedDataProvider::ConsumeBytes(size_t num_bytes) { + num_bytes = std::min(num_bytes, remaining_bytes_); + return ConsumeBytes<T>(num_bytes, num_bytes); +} + +// Similar to |ConsumeBytes|, but also appends the terminator value at the end +// of the resulting vector. Useful, when a mutable null-terminated C-string is +// needed, for example. But that is a rare case. Better avoid it, if possible, +// and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods. +template <typename T> +std::vector<T> FuzzedDataProvider::ConsumeBytesWithTerminator(size_t num_bytes, + T terminator) { + num_bytes = std::min(num_bytes, remaining_bytes_); + std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes); + result.back() = terminator; + return result; +} + +// Returns a std::vector containing all remaining bytes of the input data. +template <typename T> +std::vector<T> FuzzedDataProvider::ConsumeRemainingBytes() { + return ConsumeBytes<T>(remaining_bytes_); +} + +// Returns a std::string containing |num_bytes| of input data. Using this and +// |.c_str()| on the resulting string is the best way to get an immutable +// null-terminated C string. If fewer than |num_bytes| of data remain, returns +// a shorter std::string containing all of the data that's left. +inline std::string FuzzedDataProvider::ConsumeBytesAsString(size_t num_bytes) { + static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), + "ConsumeBytesAsString cannot convert the data to a string."); + + num_bytes = std::min(num_bytes, remaining_bytes_); + std::string result( + reinterpret_cast<const std::string::value_type *>(data_ptr_), num_bytes); + Advance(num_bytes); + return result; +} + +// Returns a std::string of length from 0 to |max_length|. When it runs out of +// input data, returns what remains of the input. Designed to be more stable +// with respect to a fuzzer inserting characters than just picking a random +// length and then consuming that many bytes with |ConsumeBytes|. +inline std::string +FuzzedDataProvider::ConsumeRandomLengthString(size_t max_length) { + // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" + // followed by anything else to the end of the string. As a result of this + // logic, a fuzzer can insert characters into the string, and the string + // will be lengthened to include those new characters, resulting in a more + // stable fuzzer than picking the length of a string independently from + // picking its contents. + std::string result; + + // Reserve the anticipated capaticity to prevent several reallocations. + result.reserve(std::min(max_length, remaining_bytes_)); + for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { + char next = ConvertUnsignedToSigned<char>(data_ptr_[0]); + Advance(1); + if (next == '\\' && remaining_bytes_ != 0) { + next = ConvertUnsignedToSigned<char>(data_ptr_[0]); + Advance(1); + if (next != '\\') + break; + } + result += next; } - // Returns a floating point value in the range [Type's lowest, Type's max] by - // consuming bytes from the input data. If there's no input data left, always - // returns approximately 0. - template <typename T> T ConsumeFloatingPoint() { - return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(), - std::numeric_limits<T>::max()); + result.shrink_to_fit(); + return result; +} + +// Returns a std::string of length from 0 to |remaining_bytes_|. +inline std::string FuzzedDataProvider::ConsumeRandomLengthString() { + return ConsumeRandomLengthString(remaining_bytes_); +} + +// Returns a std::string containing all remaining bytes of the input data. +// Prefer using |ConsumeRemainingBytes| unless you actually need a std::string +// object. +inline std::string FuzzedDataProvider::ConsumeRemainingBytesAsString() { + return ConsumeBytesAsString(remaining_bytes_); +} + +// Returns a number in the range [Type's min, Type's max]. The value might +// not be uniformly distributed in the given range. If there's no input data +// left, always returns |min|. +template <typename T> T FuzzedDataProvider::ConsumeIntegral() { + return ConsumeIntegralInRange(std::numeric_limits<T>::min(), + std::numeric_limits<T>::max()); +} + +// Returns a number in the range [min, max] by consuming bytes from the +// input data. The value might not be uniformly distributed in the given +// range. If there's no input data left, always returns |min|. |min| must +// be less than or equal to |max|. +template <typename T> +T FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) { + static_assert(std::is_integral<T>::value, "An integral type is required."); + static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type."); + + if (min > max) + abort(); + + // Use the biggest type possible to hold the range and the result. + uint64_t range = static_cast<uint64_t>(max) - min; + uint64_t result = 0; + size_t offset = 0; + + while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && + remaining_bytes_ != 0) { + // Pull bytes off the end of the seed data. Experimentally, this seems to + // allow the fuzzer to more easily explore the input space. This makes + // sense, since it works by modifying inputs that caused new code to run, + // and this data is often used to encode length of data read by + // |ConsumeBytes|. Separating out read lengths makes it easier modify the + // contents of the data that is actually read. + --remaining_bytes_; + result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; + offset += CHAR_BIT; } - // Returns a floating point value in the given range by consuming bytes from - // the input data. If there's no input data left, returns |min|. Note that - // |min| must be less than or equal to |max|. - template <typename T> T ConsumeFloatingPointInRange(T min, T max) { - if (min > max) - abort(); - - T range = .0; - T result = min; - constexpr T zero(.0); - if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) { - // The diff |max - min| would overflow the given floating point type. Use - // the half of the diff as the range and consume a bool to decide whether - // the result is in the first of the second part of the diff. - range = (max / 2.0) - (min / 2.0); - if (ConsumeBool()) { - result += range; - } - } else { - range = max - min; + // Avoid division by 0, in case |range + 1| results in overflow. + if (range != std::numeric_limits<decltype(range)>::max()) + result = result % (range + 1); + + return static_cast<T>(min + result); +} + +// Returns a floating point value in the range [Type's lowest, Type's max] by +// consuming bytes from the input data. If there's no input data left, always +// returns approximately 0. +template <typename T> T FuzzedDataProvider::ConsumeFloatingPoint() { + return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(), + std::numeric_limits<T>::max()); +} + +// Returns a floating point value in the given range by consuming bytes from +// the input data. If there's no input data left, returns |min|. Note that +// |min| must be less than or equal to |max|. +template <typename T> +T FuzzedDataProvider::ConsumeFloatingPointInRange(T min, T max) { + if (min > max) + abort(); + + T range = .0; + T result = min; + constexpr T zero(.0); + if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) { + // The diff |max - min| would overflow the given floating point type. Use + // the half of the diff as the range and consume a bool to decide whether + // the result is in the first of the second part of the diff. + range = (max / 2.0) - (min / 2.0); + if (ConsumeBool()) { + result += range; } - - return result + range * ConsumeProbability<T>(); + } else { + range = max - min; } - // Reports the remaining bytes available for fuzzed input. - size_t remaining_bytes() { return remaining_bytes_; } - - private: - FuzzedDataProvider(const FuzzedDataProvider &) = delete; - FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; - - void Advance(size_t num_bytes) { - if (num_bytes > remaining_bytes_) + return result + range * ConsumeProbability<T>(); +} + +// Returns a floating point number in the range [0.0, 1.0]. If there's no +// input data left, always returns 0. +template <typename T> T FuzzedDataProvider::ConsumeProbability() { + static_assert(std::is_floating_point<T>::value, + "A floating point type is required."); + + // Use different integral types for different floating point types in order + // to provide better density of the resulting values. + using IntegralType = + typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t, + uint64_t>::type; + + T result = static_cast<T>(ConsumeIntegral<IntegralType>()); + result /= static_cast<T>(std::numeric_limits<IntegralType>::max()); + return result; +} + +// Reads one byte and returns a bool, or false when no data remains. +inline bool FuzzedDataProvider::ConsumeBool() { + return 1 & ConsumeIntegral<uint8_t>(); +} + +// Returns an enum value. The enum must start at 0 and be contiguous. It must +// also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: +// enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; +template <typename T> T FuzzedDataProvider::ConsumeEnum() { + static_assert(std::is_enum<T>::value, "|T| must be an enum type."); + return static_cast<T>( + ConsumeIntegralInRange<uint32_t>(0, static_cast<uint32_t>(T::kMaxValue))); +} + +// Returns a copy of the value selected from the given fixed-size |array|. +template <typename T, size_t size> +T FuzzedDataProvider::PickValueInArray(const T (&array)[size]) { + static_assert(size > 0, "The array must be non empty."); + return array[ConsumeIntegralInRange<size_t>(0, size - 1)]; +} + +template <typename T, size_t size> +T FuzzedDataProvider::PickValueInArray(const std::array<T, size> &array) { + static_assert(size > 0, "The array must be non empty."); + return array[ConsumeIntegralInRange<size_t>(0, size - 1)]; +} + +template <typename T> +T FuzzedDataProvider::PickValueInArray(std::initializer_list<const T> list) { + // TODO(Dor1s): switch to static_assert once C++14 is allowed. + if (!list.size()) + abort(); + + return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1)); +} + +// Writes |num_bytes| of input data to the given destination pointer. If there +// is not enough data left, writes all remaining bytes. Return value is the +// number of bytes written. +// In general, it's better to avoid using this function, but it may be useful +// in cases when it's necessary to fill a certain buffer or object with +// fuzzing data. +inline size_t FuzzedDataProvider::ConsumeData(void *destination, + size_t num_bytes) { + num_bytes = std::min(num_bytes, remaining_bytes_); + CopyAndAdvance(destination, num_bytes); + return num_bytes; +} + +// Private methods. +inline void FuzzedDataProvider::CopyAndAdvance(void *destination, + size_t num_bytes) { + std::memcpy(destination, data_ptr_, num_bytes); + Advance(num_bytes); +} + +inline void FuzzedDataProvider::Advance(size_t num_bytes) { + if (num_bytes > remaining_bytes_) + abort(); + + data_ptr_ += num_bytes; + remaining_bytes_ -= num_bytes; +} + +template <typename T> +std::vector<T> FuzzedDataProvider::ConsumeBytes(size_t size, size_t num_bytes) { + static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type."); + + // The point of using the size-based constructor below is to increase the + // odds of having a vector object with capacity being equal to the length. + // That part is always implementation specific, but at least both libc++ and + // libstdc++ allocate the requested number of bytes in that constructor, + // which seems to be a natural choice for other implementations as well. + // To increase the odds even more, we also call |shrink_to_fit| below. + std::vector<T> result(size); + if (size == 0) { + if (num_bytes != 0) abort(); - - data_ptr_ += num_bytes; - remaining_bytes_ -= num_bytes; - } - - template <typename T> - std::vector<T> ConsumeBytes(size_t size, size_t num_bytes_to_consume) { - static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type."); - - // The point of using the size-based constructor below is to increase the - // odds of having a vector object with capacity being equal to the length. - // That part is always implementation specific, but at least both libc++ and - // libstdc++ allocate the requested number of bytes in that constructor, - // which seems to be a natural choice for other implementations as well. - // To increase the odds even more, we also call |shrink_to_fit| below. - std::vector<T> result(size); - if (size == 0) { - if (num_bytes_to_consume != 0) - abort(); - return result; - } - - std::memcpy(result.data(), data_ptr_, num_bytes_to_consume); - Advance(num_bytes_to_consume); - - // Even though |shrink_to_fit| is also implementation specific, we expect it - // to provide an additional assurance in case vector's constructor allocated - // a buffer which is larger than the actual amount of data we put inside it. - result.shrink_to_fit(); return result; } - template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value) { - static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); - static_assert(!std::numeric_limits<TU>::is_signed, - "Source type must be unsigned."); - - // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. - if (std::numeric_limits<TS>::is_modulo) - return static_cast<TS>(value); - - // Avoid using implementation-defined unsigned to signer conversions. - // To learn more, see https://stackoverflow.com/questions/13150449. - if (value <= std::numeric_limits<TS>::max()) { - return static_cast<TS>(value); - } else { - constexpr auto TS_min = std::numeric_limits<TS>::min(); - return TS_min + static_cast<char>(value - TS_min); - } + CopyAndAdvance(result.data(), num_bytes); + + // Even though |shrink_to_fit| is also implementation specific, we expect it + // to provide an additional assurance in case vector's constructor allocated + // a buffer which is larger than the actual amount of data we put inside it. + result.shrink_to_fit(); + return result; +} + +template <typename TS, typename TU> +TS FuzzedDataProvider::ConvertUnsignedToSigned(TU value) { + static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); + static_assert(!std::numeric_limits<TU>::is_signed, + "Source type must be unsigned."); + + // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. + if (std::numeric_limits<TS>::is_modulo) + return static_cast<TS>(value); + + // Avoid using implementation-defined unsigned to signed conversions. + // To learn more, see https://stackoverflow.com/questions/13150449. + if (value <= std::numeric_limits<TS>::max()) { + return static_cast<TS>(value); + } else { + constexpr auto TS_min = std::numeric_limits<TS>::min(); + return TS_min + static_cast<char>(value - TS_min); } - - const uint8_t *data_ptr_; - size_t remaining_bytes_; -}; +} #endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ diff --git a/src/test/fuzz/addition_overflow.cpp b/src/test/fuzz/addition_overflow.cpp index a455992b13..c6cfbd8d30 100644 --- a/src/test/fuzz/addition_overflow.cpp +++ b/src/test/fuzz/addition_overflow.cpp @@ -14,7 +14,7 @@ #if __has_builtin(__builtin_add_overflow) #define HAVE_BUILTIN_ADD_OVERFLOW #endif -#elif defined(__GNUC__) && (__GNUC__ >= 5) +#elif defined(__GNUC__) #define HAVE_BUILTIN_ADD_OVERFLOW #endif @@ -40,7 +40,7 @@ void TestAdditionOverflow(FuzzedDataProvider& fuzzed_data_provider) } } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(addition_overflow) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); TestAdditionOverflow<int64_t>(fuzzed_data_provider); diff --git a/src/test/fuzz/addrdb.cpp b/src/test/fuzz/addrdb.cpp index 16b1cb755a..d15c785673 100644 --- a/src/test/fuzz/addrdb.cpp +++ b/src/test/fuzz/addrdb.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(addrdb) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 0ceeea2d36..1ea6b3d01d 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -17,17 +17,27 @@ #include <string> #include <vector> -void initialize() +void initialize_addrman() { SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +class CAddrManDeterministic : public CAddrMan { - FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); +public: + void MakeDeterministic(const uint256& random_seed) + { + insecure_rand = FastRandomContext{random_seed}; + Clear(); + } +}; +FUZZ_TARGET_INIT(addrman, initialize_addrman) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); SetMockTime(ConsumeTime(fuzzed_data_provider)); - CAddrMan addr_man; + CAddrManDeterministic addr_man; + addr_man.MakeDeterministic(ConsumeUInt256(fuzzed_data_provider)); if (fuzzed_data_provider.ConsumeBool()) { addr_man.m_asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); if (!SanityCheckASMap(addr_man.m_asmap)) { @@ -35,83 +45,71 @@ void test_one_input(const std::vector<uint8_t>& buffer) } } while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) { - case 0: { - addr_man.Clear(); - break; - } - case 1: { - addr_man.ResolveCollisions(); - break; - } - case 2: { - (void)addr_man.SelectTriedCollision(); - break; - } - case 3: { - (void)addr_man.Select(fuzzed_data_provider.ConsumeBool()); - break; - } - case 4: { - (void)addr_man.GetAddr(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - break; - } - case 5: { - const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); - const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); - if (opt_address && opt_net_addr) { - addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000)); - } - break; - } - case 6: { - std::vector<CAddress> addresses; - while (fuzzed_data_provider.ConsumeBool()) { + CallOneOf( + fuzzed_data_provider, + [&] { + addr_man.Clear(); + }, + [&] { + addr_man.ResolveCollisions(); + }, + [&] { + (void)addr_man.SelectTriedCollision(); + }, + [&] { + (void)addr_man.Select(fuzzed_data_provider.ConsumeBool()); + }, + [&] { + (void)addr_man.GetAddr(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + }, + [&] { const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); - if (!opt_address) { - break; + const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); + if (opt_address && opt_net_addr) { + addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000)); } - addresses.push_back(*opt_address); - } - const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); - if (opt_net_addr) { - addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000)); - } - break; - } - case 7: { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); - } - break; - } - case 8: { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); - } - break; - } - case 9: { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider)); - } - break; - } - case 10: { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()}); - } - break; - } - case 11: { - (void)addr_man.Check(); - break; - } - } + }, + [&] { + std::vector<CAddress> addresses; + while (fuzzed_data_provider.ConsumeBool()) { + const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); + if (!opt_address) { + break; + } + addresses.push_back(*opt_address); + } + const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); + if (opt_net_addr) { + addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000)); + } + }, + [&] { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + } + }, + [&] { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + } + }, + [&] { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider)); + } + }, + [&] { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()}); + } + }, + [&] { + (void)addr_man.Check(); + }); } (void)addr_man.size(); CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp index e3aefa18a3..4c5bc0cbf2 100644 --- a/src/test/fuzz/asmap.cpp +++ b/src/test/fuzz/asmap.cpp @@ -27,7 +27,7 @@ static const std::vector<bool> IPV4_PREFIX_ASMAP = { true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true // Match 0xFF }; -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(asmap) { // Encoding: [7 bits: asmap size] [1 bit: ipv6?] [3-130 bytes: asmap] [4 or 16 bytes: addr] if (buffer.size() < 1 + 3 + 4) return; diff --git a/src/test/fuzz/asmap_direct.cpp b/src/test/fuzz/asmap_direct.cpp index 2d21eff9d6..8b7822dc16 100644 --- a/src/test/fuzz/asmap_direct.cpp +++ b/src/test/fuzz/asmap_direct.cpp @@ -11,7 +11,7 @@ #include <assert.h> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(asmap_direct) { // Encoding: [asmap using 1 bit / byte] 0xFF [addr using 1 bit / byte] std::optional<size_t> sep_pos_opt; diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp index 7ea0bdd2a7..9ecd172e19 100644 --- a/src/test/fuzz/autofile.cpp +++ b/src/test/fuzz/autofile.cpp @@ -15,49 +15,43 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(autofile) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); CAutoFile auto_file = fuzzed_auto_file_provider.open(); while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) { - case 0: { - std::array<uint8_t, 4096> arr{}; - try { - auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 1: { - const std::array<uint8_t, 4096> arr{}; - try { - auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 2: { - try { - auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 3: { - auto_file.fclose(); - break; - } - case 4: { - ReadFromStream(fuzzed_data_provider, auto_file); - break; - } - case 5: { - WriteToStream(fuzzed_data_provider, auto_file); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + std::array<uint8_t, 4096> arr{}; + try { + auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + const std::array<uint8_t, 4096> arr{}; + try { + auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + try { + auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + auto_file.fclose(); + }, + [&] { + ReadFromStream(fuzzed_data_provider, auto_file); + }, + [&] { + WriteToStream(fuzzed_data_provider, auto_file); + }); } (void)auto_file.Get(); (void)auto_file.GetType(); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index fc4a1d9261..e0715f3e29 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -24,64 +24,57 @@ int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept } } // namespace -void initialize() +void initialize_banman() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeFuzzingContext<>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(banman, initialize_banman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); const fs::path banlist_file = GetDataDir() / "fuzzed_banlist.dat"; fs::remove(banlist_file); { BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)}; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) { - case 0: { - ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider), - ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); - break; - } - case 1: { - ban_man.Ban(ConsumeSubNet(fuzzed_data_provider), - ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); - break; - } - case 2: { - ban_man.ClearBanned(); - break; - } - case 4: { - ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider)); - break; - } - case 5: { - ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider)); - break; - } - case 6: { - ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider)); - break; - } - case 7: { - ban_man.Unban(ConsumeSubNet(fuzzed_data_provider)); - break; - } - case 8: { - banmap_t banmap; - ban_man.GetBanned(banmap); - break; - } - case 9: { - ban_man.DumpBanlist(); - break; - } - case 11: { - ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider)); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider), + ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); + }, + [&] { + ban_man.Ban(ConsumeSubNet(fuzzed_data_provider), + ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); + }, + [&] { + ban_man.ClearBanned(); + }, + [] {}, + [&] { + ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider)); + }, + [&] { + ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider)); + }, + [&] { + ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider)); + }, + [&] { + ban_man.Unban(ConsumeSubNet(fuzzed_data_provider)); + }, + [&] { + banmap_t banmap; + ban_man.GetBanned(banmap); + }, + [&] { + ban_man.DumpBanlist(); + }, + [] {}, + [&] { + ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider)); + }); } } fs::remove(banlist_file); diff --git a/src/test/fuzz/base_encode_decode.cpp b/src/test/fuzz/base_encode_decode.cpp index 8d49f93c2f..4470e13a61 100644 --- a/src/test/fuzz/base_encode_decode.cpp +++ b/src/test/fuzz/base_encode_decode.cpp @@ -14,7 +14,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(base_encode_decode) { const std::string random_encoded_string(buffer.begin(), buffer.end()); diff --git a/src/test/fuzz/bech32.cpp b/src/test/fuzz/bech32.cpp index 8b91f9bc96..95cd4b413f 100644 --- a/src/test/fuzz/bech32.cpp +++ b/src/test/fuzz/bech32.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -13,7 +13,7 @@ #include <utility> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(bech32) { const std::string random_string(buffer.begin(), buffer.end()); const std::pair<std::string, std::vector<uint8_t>> r1 = bech32::Decode(random_string); diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp index 91bd34a251..65a33de4b4 100644 --- a/src/test/fuzz/block.cpp +++ b/src/test/fuzz/block.cpp @@ -17,13 +17,13 @@ #include <cassert> #include <string> -void initialize() +void initialize_block() { static const ECCVerifyHandle verify_handle; SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(block, initialize_block) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); CBlock block; diff --git a/src/test/fuzz/block_header.cpp b/src/test/fuzz/block_header.cpp index 09c2b4a951..c73270dcb3 100644 --- a/src/test/fuzz/block_header.cpp +++ b/src/test/fuzz/block_header.cpp @@ -14,7 +14,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(block_header) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider); diff --git a/src/test/fuzz/blockfilter.cpp b/src/test/fuzz/blockfilter.cpp index 7232325a20..7fa06085f8 100644 --- a/src/test/fuzz/blockfilter.cpp +++ b/src/test/fuzz/blockfilter.cpp @@ -12,7 +12,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(blockfilter) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::optional<BlockFilter> block_filter = ConsumeDeserializable<BlockFilter>(fuzzed_data_provider); diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp index d955c71bc9..d43c182644 100644 --- a/src/test/fuzz/bloom_filter.cpp +++ b/src/test/fuzz/bloom_filter.cpp @@ -15,7 +15,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(bloom_filter) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); @@ -25,47 +25,43 @@ void test_one_input(const std::vector<uint8_t>& buffer) fuzzed_data_provider.ConsumeIntegral<unsigned int>(), static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))}; while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 3)) { - case 0: { - const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); - (void)bloom_filter.contains(b); - bloom_filter.insert(b); - const bool present = bloom_filter.contains(b); - assert(present); - break; - } - case 1: { - const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); - if (!out_point) { - break; - } - (void)bloom_filter.contains(*out_point); - bloom_filter.insert(*out_point); - const bool present = bloom_filter.contains(*out_point); - assert(present); - break; - } - case 2: { - const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider); - if (!u256) { - break; - } - (void)bloom_filter.contains(*u256); - bloom_filter.insert(*u256); - const bool present = bloom_filter.contains(*u256); - assert(present); - break; - } - case 3: { - const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); - if (!mut_tx) { - break; - } - const CTransaction tx{*mut_tx}; - (void)bloom_filter.IsRelevantAndUpdate(tx); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + (void)bloom_filter.contains(b); + bloom_filter.insert(b); + const bool present = bloom_filter.contains(b); + assert(present); + }, + [&] { + const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); + if (!out_point) { + return; + } + (void)bloom_filter.contains(*out_point); + bloom_filter.insert(*out_point); + const bool present = bloom_filter.contains(*out_point); + assert(present); + }, + [&] { + const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider); + if (!u256) { + return; + } + (void)bloom_filter.contains(*u256); + bloom_filter.insert(*u256); + const bool present = bloom_filter.contains(*u256); + assert(present); + }, + [&] { + const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); + if (!mut_tx) { + return; + } + const CTransaction tx{*mut_tx}; + (void)bloom_filter.IsRelevantAndUpdate(tx); + }); (void)bloom_filter.IsWithinSizeConstraints(); } } diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp index e575640be5..3a1b2dbbe7 100644 --- a/src/test/fuzz/buffered_file.cpp +++ b/src/test/fuzz/buffered_file.cpp @@ -15,7 +15,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(buffered_file) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); @@ -31,41 +31,36 @@ void test_one_input(const std::vector<uint8_t>& buffer) if (opt_buffered_file && fuzzed_file != nullptr) { bool setpos_fail = false; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 4)) { - case 0: { - std::array<uint8_t, 4096> arr{}; - try { - opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 1: { - opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096)); - break; - } - case 2: { - if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096))) { - setpos_fail = true; - } - break; - } - case 3: { - if (setpos_fail) { - // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop. - break; - } - try { - opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>()); - } catch (const std::ios_base::failure&) { - } - break; - } - case 4: { - ReadFromStream(fuzzed_data_provider, *opt_buffered_file); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + std::array<uint8_t, 4096> arr{}; + try { + opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096)); + }, + [&] { + if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096))) { + setpos_fail = true; + } + }, + [&] { + if (setpos_fail) { + // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop. + return; + } + try { + opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>()); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + ReadFromStream(fuzzed_data_provider, *opt_buffered_file); + }); } opt_buffered_file->GetPos(); opt_buffered_file->GetType(); diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp index 47c71850ce..9f7074b423 100644 --- a/src/test/fuzz/chain.cpp +++ b/src/test/fuzz/chain.cpp @@ -11,7 +11,7 @@ #include <optional> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(chain) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); std::optional<CDiskBlockIndex> disk_block_index = ConsumeDeserializable<CDiskBlockIndex>(fuzzed_data_provider); diff --git a/src/test/fuzz/checkqueue.cpp b/src/test/fuzz/checkqueue.cpp index c69043bb6b..0b16f0f0d5 100644 --- a/src/test/fuzz/checkqueue.cpp +++ b/src/test/fuzz/checkqueue.cpp @@ -32,7 +32,7 @@ struct DumbCheck { }; } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(checkqueue) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index ac034809b0..8ece94d771 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -34,14 +34,12 @@ bool operator==(const Coin& a, const Coin& b) } } // namespace -void initialize() +void initialize_coins_view() { - static const ECCVerifyHandle ecc_verify_handle; - ECC_Start(); - SelectParams(CBaseChainParams::REGTEST); + static const auto testing_setup = MakeFuzzingContext<const TestingSetup>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(coins_view, initialize_coins_view) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CCoinsView backend_coins_view; @@ -50,103 +48,93 @@ void test_one_input(const std::vector<uint8_t>& buffer) Coin random_coin; CMutableTransaction random_mutable_transaction; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) { - case 0: { - if (random_coin.IsSpent()) { - break; - } - Coin coin = random_coin; - bool expected_code_path = false; - const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); - try { - coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite); - expected_code_path = true; - } catch (const std::logic_error& e) { - if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { - assert(!possible_overwrite); + CallOneOf( + fuzzed_data_provider, + [&] { + if (random_coin.IsSpent()) { + return; + } + Coin coin = random_coin; + bool expected_code_path = false; + const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); + try { + coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite); expected_code_path = true; + } catch (const std::logic_error& e) { + if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { + assert(!possible_overwrite); + expected_code_path = true; + } } - } - assert(expected_code_path); - break; - } - case 1: { - (void)coins_view_cache.Flush(); - break; - } - case 2: { - coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider)); - break; - } - case 3: { - Coin move_to; - (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr); - break; - } - case 4: { - coins_view_cache.Uncache(random_out_point); - break; - } - case 5: { - if (fuzzed_data_provider.ConsumeBool()) { - backend_coins_view = CCoinsView{}; - } - coins_view_cache.SetBackend(backend_coins_view); - break; - } - case 6: { - const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); - if (!opt_out_point) { - break; - } - random_out_point = *opt_out_point; - break; - } - case 7: { - const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); - if (!opt_coin) { - break; - } - random_coin = *opt_coin; - break; - } - case 8: { - const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); - if (!opt_mutable_transaction) { - break; - } - random_mutable_transaction = *opt_mutable_transaction; - break; - } - case 9: { - CCoinsMap coins_map; - while (fuzzed_data_provider.ConsumeBool()) { - CCoinsCacheEntry coins_cache_entry; - coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>(); + assert(expected_code_path); + }, + [&] { + (void)coins_view_cache.Flush(); + }, + [&] { + coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider)); + }, + [&] { + Coin move_to; + (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr); + }, + [&] { + coins_view_cache.Uncache(random_out_point); + }, + [&] { if (fuzzed_data_provider.ConsumeBool()) { - coins_cache_entry.coin = random_coin; - } else { - const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); - if (!opt_coin) { - break; + backend_coins_view = CCoinsView{}; + } + coins_view_cache.SetBackend(backend_coins_view); + }, + [&] { + const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); + if (!opt_out_point) { + return; + } + random_out_point = *opt_out_point; + }, + [&] { + const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); + if (!opt_coin) { + return; + } + random_coin = *opt_coin; + }, + [&] { + const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); + if (!opt_mutable_transaction) { + return; + } + random_mutable_transaction = *opt_mutable_transaction; + }, + [&] { + CCoinsMap coins_map; + while (fuzzed_data_provider.ConsumeBool()) { + CCoinsCacheEntry coins_cache_entry; + coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>(); + if (fuzzed_data_provider.ConsumeBool()) { + coins_cache_entry.coin = random_coin; + } else { + const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); + if (!opt_coin) { + return; + } + coins_cache_entry.coin = *opt_coin; } - coins_cache_entry.coin = *opt_coin; + coins_map.emplace(random_out_point, std::move(coins_cache_entry)); } - coins_map.emplace(random_out_point, std::move(coins_cache_entry)); - } - bool expected_code_path = false; - try { - coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock()); - expected_code_path = true; - } catch (const std::logic_error& e) { - if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) { + bool expected_code_path = false; + try { + coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock()); expected_code_path = true; + } catch (const std::logic_error& e) { + if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) { + expected_code_path = true; + } } - } - assert(expected_code_path); - break; - } - } + assert(expected_code_path); + }); } { @@ -199,97 +187,90 @@ void test_one_input(const std::vector<uint8_t>& buffer) } if (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) { - case 0: { - const CTransaction transaction{random_mutable_transaction}; - bool is_spent = false; - for (const CTxOut& tx_out : transaction.vout) { - if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { - is_spent = true; + CallOneOf( + fuzzed_data_provider, + [&] { + const CTransaction transaction{random_mutable_transaction}; + bool is_spent = false; + for (const CTxOut& tx_out : transaction.vout) { + if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { + is_spent = true; + } + } + if (is_spent) { + // Avoid: + // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed. + return; } - } - if (is_spent) { - // Avoid: - // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed. - break; - } - bool expected_code_path = false; - const int height = fuzzed_data_provider.ConsumeIntegral<int>(); - const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); - try { - AddCoins(coins_view_cache, transaction, height, possible_overwrite); - expected_code_path = true; - } catch (const std::logic_error& e) { - if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { - assert(!possible_overwrite); + bool expected_code_path = false; + const int height = fuzzed_data_provider.ConsumeIntegral<int>(); + const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); + try { + AddCoins(coins_view_cache, transaction, height, possible_overwrite); expected_code_path = true; + } catch (const std::logic_error& e) { + if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { + assert(!possible_overwrite); + expected_code_path = true; + } } - } - assert(expected_code_path); - break; - } - case 1: { - (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, false); - (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, true); - break; - } - case 2: { - TxValidationState state; - CAmount tx_fee_out; - const CTransaction transaction{random_mutable_transaction}; - if (ContainsSpentInput(transaction, coins_view_cache)) { - // Avoid: - // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. - break; - } - try { - (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out); - assert(MoneyRange(tx_fee_out)); - } catch (const std::runtime_error&) { - } - break; - } - case 3: { - const CTransaction transaction{random_mutable_transaction}; - if (ContainsSpentInput(transaction, coins_view_cache)) { - // Avoid: - // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. - break; - } - (void)GetP2SHSigOpCount(transaction, coins_view_cache); - break; - } - case 4: { - const CTransaction transaction{random_mutable_transaction}; - if (ContainsSpentInput(transaction, coins_view_cache)) { - // Avoid: - // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. - break; - } - const int flags = fuzzed_data_provider.ConsumeIntegral<int>(); - if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) { - // Avoid: - // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed. - break; - } - (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags); - break; - } - case 5: { - CCoinsStats stats; - bool expected_code_path = false; - try { - (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED); - } catch (const std::logic_error&) { - expected_code_path = true; - } - assert(expected_code_path); - break; - } - case 6: { - (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache); - break; - } - } + assert(expected_code_path); + }, + [&] { + (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, false); + (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, true); + }, + [&] { + TxValidationState state; + CAmount tx_fee_out; + const CTransaction transaction{random_mutable_transaction}; + if (ContainsSpentInput(transaction, coins_view_cache)) { + // Avoid: + // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. + return; + } + try { + (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out); + assert(MoneyRange(tx_fee_out)); + } catch (const std::runtime_error&) { + } + }, + [&] { + const CTransaction transaction{random_mutable_transaction}; + if (ContainsSpentInput(transaction, coins_view_cache)) { + // Avoid: + // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. + return; + } + (void)GetP2SHSigOpCount(transaction, coins_view_cache); + }, + [&] { + const CTransaction transaction{random_mutable_transaction}; + if (ContainsSpentInput(transaction, coins_view_cache)) { + // Avoid: + // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. + return; + } + const int flags = fuzzed_data_provider.ConsumeIntegral<int>(); + if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) { + // Avoid: + // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed. + return; + } + (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags); + }, + [&] { + CCoinsStats stats; + bool expected_code_path = false; + try { + (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED); + } catch (const std::logic_error&) { + expected_code_path = true; + } + assert(expected_code_path); + }, + [&] { + (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache); + }); } } diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 6521c3f3b2..71b4b00116 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -15,14 +15,15 @@ #include <cstdint> #include <vector> -void initialize() +void initialize_connman() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeFuzzingContext<>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(connman, initialize_connman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeBool()}; CAddress random_address; CNetAddr random_netaddr; @@ -31,121 +32,107 @@ void test_one_input(const std::vector<uint8_t>& buffer) CSubNet random_subnet; std::string random_string; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 30)) { - case 0: - random_address = ConsumeAddress(fuzzed_data_provider); - break; - case 1: - random_netaddr = ConsumeNetAddr(fuzzed_data_provider); - break; - case 2: - random_service = ConsumeService(fuzzed_data_provider); - break; - case 3: - random_subnet = ConsumeSubNet(fuzzed_data_provider); - break; - case 4: - random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); - break; - case 5: { - std::vector<CAddress> addresses; - while (fuzzed_data_provider.ConsumeBool()) { - addresses.push_back(ConsumeAddress(fuzzed_data_provider)); - } - // Limit nTimePenalty to int32_t to avoid signed integer overflow - (void)connman.AddNewAddresses(addresses, ConsumeAddress(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()); - break; - } - case 6: - connman.AddNode(random_string); - break; - case 7: - connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - case 8: - connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral<NodeId>()); - break; - case 9: - connman.DisconnectNode(random_netaddr); - break; - case 10: - connman.DisconnectNode(random_string); - break; - case 11: - connman.DisconnectNode(random_subnet); - break; - case 12: - connman.ForEachNode([](auto) {}); - break; - case 13: - connman.ForEachNodeThen([](auto) {}, []() {}); - break; - case 14: - (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); - break; - case 15: - (void)connman.GetAddresses(fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); - break; - case 16: { - (void)connman.GetAddresses(random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); - break; - } - case 17: - (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - case 18: - (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); - break; - case 19: - connman.MarkAddressGood(random_address); - break; - case 20: - (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool()); - break; - case 21: - // Limit now to int32_t to avoid signed integer overflow - (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>()); - break; - case 22: { - CSerializedNetMsg serialized_net_msg; - serialized_net_msg.m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE); - serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); - connman.PushMessage(&random_node, std::move(serialized_net_msg)); - break; - } - case 23: - connman.RemoveAddedNode(random_string); - break; - case 24: { - const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); - if (SanityCheckASMap(asmap)) { - connman.SetAsmap(asmap); - } - break; - } - case 25: - connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral<int>()); - break; - case 26: - connman.SetMaxOutboundTarget(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - case 27: - connman.SetMaxOutboundTimeframe(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - case 28: - connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool()); - break; - case 29: - connman.SetServices(random_service, static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>())); - break; - case 30: - connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool()); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + random_address = ConsumeAddress(fuzzed_data_provider); + }, + [&] { + random_netaddr = ConsumeNetAddr(fuzzed_data_provider); + }, + [&] { + random_service = ConsumeService(fuzzed_data_provider); + }, + [&] { + random_subnet = ConsumeSubNet(fuzzed_data_provider); + }, + [&] { + random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); + }, + [&] { + std::vector<CAddress> addresses; + while (fuzzed_data_provider.ConsumeBool()) { + addresses.push_back(ConsumeAddress(fuzzed_data_provider)); + } + // Limit nTimePenalty to int32_t to avoid signed integer overflow + (void)connman.AddNewAddresses(addresses, ConsumeAddress(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()); + }, + [&] { + connman.AddNode(random_string); + }, + [&] { + connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + }, + [&] { + connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral<NodeId>()); + }, + [&] { + connman.DisconnectNode(random_netaddr); + }, + [&] { + connman.DisconnectNode(random_string); + }, + [&] { + connman.DisconnectNode(random_subnet); + }, + [&] { + connman.ForEachNode([](auto) {}); + }, + [&] { + connman.ForEachNodeThen([](auto) {}, []() {}); + }, + [&] { + (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); + }, + [&] { + (void)connman.GetAddresses(fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); + }, + [&] { + (void)connman.GetAddresses(random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); + }, + [&] { + (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + }, + [&] { + (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); + }, + [&] { + connman.MarkAddressGood(random_address); + }, + [&] { + (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool()); + }, + [&] { + // Limit now to int32_t to avoid signed integer overflow + (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>()); + }, + [&] { + CSerializedNetMsg serialized_net_msg; + serialized_net_msg.m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE); + serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + connman.PushMessage(&random_node, std::move(serialized_net_msg)); + }, + [&] { + connman.RemoveAddedNode(random_string); + }, + [&] { + const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); + if (SanityCheckASMap(asmap)) { + connman.SetAsmap(asmap); + } + }, + [&] { + connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool()); + }, + [&] { + connman.SetServices(random_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); + }, + [&] { + connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool()); + }); } (void)connman.GetAddedNodeInfo(); - (void)connman.GetBestHeight(); - (void)connman.GetExtraOutboundCount(); + (void)connman.GetExtraFullOutboundCount(); (void)connman.GetLocalServices(); (void)connman.GetMaxOutboundTarget(); (void)connman.GetMaxOutboundTimeframe(); diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp index 664e65accc..c2bb3a1a4e 100644 --- a/src/test/fuzz/crypto.cpp +++ b/src/test/fuzz/crypto.cpp @@ -4,6 +4,7 @@ #include <crypto/hmac_sha256.h> #include <crypto/hmac_sha512.h> +#include <crypto/muhash.h> #include <crypto/ripemd160.h> #include <crypto/sha1.h> #include <crypto/sha256.h> @@ -17,7 +18,7 @@ #include <cstdint> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider); @@ -35,99 +36,98 @@ void test_one_input(const std::vector<uint8_t>& buffer) CSHA512 sha512; SHA3_256 sha3; CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()}; + MuHash3072 muhash; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) { - case 0: { - if (fuzzed_data_provider.ConsumeBool()) { - data = ConsumeRandomLengthByteVector(fuzzed_data_provider); - if (data.empty()) { - data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); + CallOneOf( + fuzzed_data_provider, + [&] { + if (fuzzed_data_provider.ConsumeBool()) { + data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + if (data.empty()) { + data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); + } } - } - (void)hash160.Write(data); - (void)hash256.Write(data); - (void)hmac_sha256.Write(data.data(), data.size()); - (void)hmac_sha512.Write(data.data(), data.size()); - (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()); + (void)hash160.Write(data); + (void)hash256.Write(data); + (void)hmac_sha256.Write(data.data(), data.size()); + (void)hmac_sha512.Write(data.data(), data.size()); + (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()); - (void)Hash(data); - (void)Hash160(data); - (void)sha512.Size(); - break; - } - case 1: { - (void)hash160.Reset(); - (void)hash256.Reset(); - (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, 9)) { - case 0: { - data.resize(CHash160::OUTPUT_SIZE); - hash160.Finalize(data); - break; - } - case 1: { - data.resize(CHash256::OUTPUT_SIZE); - hash256.Finalize(data); - break; - } - case 2: { - data.resize(CHMAC_SHA256::OUTPUT_SIZE); - hmac_sha256.Finalize(data.data()); - break; - } - case 3: { - data.resize(CHMAC_SHA512::OUTPUT_SIZE); - hmac_sha512.Finalize(data.data()); - break; - } - case 4: { - data.resize(CRIPEMD160::OUTPUT_SIZE); - ripemd160.Finalize(data.data()); - break; - } - case 5: { - data.resize(CSHA1::OUTPUT_SIZE); - sha1.Finalize(data.data()); - break; - } - case 6: { - data.resize(CSHA256::OUTPUT_SIZE); - sha256.Finalize(data.data()); - break; - } - case 7: { - data.resize(CSHA512::OUTPUT_SIZE); - sha512.Finalize(data.data()); - break; - } - case 8: { - data.resize(1); - data[0] = sip_hasher.Finalize() % 256; - break; - } - case 9: { - data.resize(SHA3_256::OUTPUT_SIZE); - sha3.Finalize(data); - break; - } - } - break; - } - } + (void)Hash(data); + (void)Hash160(data); + (void)sha512.Size(); + + if (fuzzed_data_provider.ConsumeBool()) { + muhash *= MuHash3072(data); + } else { + muhash /= MuHash3072(data); + } + }, + [&] { + (void)hash160.Reset(); + (void)hash256.Reset(); + (void)ripemd160.Reset(); + (void)sha1.Reset(); + (void)sha256.Reset(); + (void)sha3.Reset(); + (void)sha512.Reset(); + muhash = MuHash3072(); + }, + [&] { + CallOneOf( + fuzzed_data_provider, + [&] { + data.resize(CHash160::OUTPUT_SIZE); + hash160.Finalize(data); + }, + [&] { + data.resize(CHash256::OUTPUT_SIZE); + hash256.Finalize(data); + }, + [&] { + data.resize(CHMAC_SHA256::OUTPUT_SIZE); + hmac_sha256.Finalize(data.data()); + }, + [&] { + data.resize(CHMAC_SHA512::OUTPUT_SIZE); + hmac_sha512.Finalize(data.data()); + }, + [&] { + data.resize(CRIPEMD160::OUTPUT_SIZE); + ripemd160.Finalize(data.data()); + }, + [&] { + data.resize(CSHA1::OUTPUT_SIZE); + sha1.Finalize(data.data()); + }, + [&] { + data.resize(CSHA256::OUTPUT_SIZE); + sha256.Finalize(data.data()); + }, + [&] { + data.resize(CSHA512::OUTPUT_SIZE); + sha512.Finalize(data.data()); + }, + [&] { + data.resize(1); + data[0] = sip_hasher.Finalize() % 256; + }, + [&] { + data.resize(SHA3_256::OUTPUT_SIZE); + sha3.Finalize(data); + }, + [&] { + uint256 out; + muhash.Finalize(out); + }); + }); } if (fuzzed_data_provider.ConsumeBool()) { uint64_t state[25]; diff --git a/src/test/fuzz/crypto_aes256.cpp b/src/test/fuzz/crypto_aes256.cpp index ae14073c96..ccabd1f7dc 100644 --- a/src/test/fuzz/crypto_aes256.cpp +++ b/src/test/fuzz/crypto_aes256.cpp @@ -11,7 +11,7 @@ #include <cstdint> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_aes256) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES256_KEYSIZE); diff --git a/src/test/fuzz/crypto_aes256cbc.cpp b/src/test/fuzz/crypto_aes256cbc.cpp index 52983c7e79..6d4138e546 100644 --- a/src/test/fuzz/crypto_aes256cbc.cpp +++ b/src/test/fuzz/crypto_aes256cbc.cpp @@ -11,7 +11,7 @@ #include <cstdint> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_aes256cbc) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES256_KEYSIZE); diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp index b7438d312d..bb8dd4594f 100644 --- a/src/test/fuzz/crypto_chacha20.cpp +++ b/src/test/fuzz/crypto_chacha20.cpp @@ -10,7 +10,7 @@ #include <cstdint> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_chacha20) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; @@ -20,31 +20,26 @@ void test_one_input(const std::vector<uint8_t>& buffer) chacha20 = ChaCha20{key.data(), key.size()}; } while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) { - case 0: { - const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32)); - chacha20.SetKey(key.data(), key.size()); - break; - } - case 1: { - chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - } - case 2: { - chacha20.Seek(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - } - case 3: { - std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - chacha20.Keystream(output.data(), output.size()); - break; - } - case 4: { - std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); - const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); - chacha20.Crypt(input.data(), output.data(), input.size()); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32)); + chacha20.SetKey(key.data(), key.size()); + }, + [&] { + chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + }, + [&] { + chacha20.Seek(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + }, + [&] { + std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + chacha20.Keystream(output.data(), output.size()); + }, + [&] { + std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); + chacha20.Crypt(input.data(), output.data(), input.size()); + }); } } diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp index 48e4263f27..0e1c44cded 100644 --- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp +++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp @@ -13,7 +13,7 @@ #include <limits> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_chacha20_poly1305_aead) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; @@ -29,44 +29,43 @@ void test_one_input(const std::vector<uint8_t>& buffer) std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); bool is_encrypt = fuzzed_data_provider.ConsumeBool(); while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) { - case 0: { - buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096); - in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - break; - } - case 1: { - (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt); - break; - } - case 2: { - uint32_t len = 0; - const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); - assert(ok); - break; - } - case 3: { - seqnr_payload += 1; - aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; - if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { - aad_pos = 0; - seqnr_aad += 1; - } - break; - } - case 4: { - seqnr_payload = fuzzed_data_provider.ConsumeIntegral<int>(); - break; - } - case 5: { - seqnr_aad = fuzzed_data_provider.ConsumeIntegral<int>(); - break; - } - case 6: { - is_encrypt = fuzzed_data_provider.ConsumeBool(); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096); + in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + }, + [&] { + (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt); + }, + [&] { + uint32_t len = 0; + const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); + assert(ok); + }, + [&] { + if (AdditionOverflow(seqnr_payload, static_cast<uint64_t>(1))) { + return; + } + seqnr_payload += 1; + aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; + if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { + aad_pos = 0; + if (AdditionOverflow(seqnr_aad, static_cast<uint64_t>(1))) { + return; + } + seqnr_aad += 1; + } + }, + [&] { + seqnr_payload = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); + }, + [&] { + seqnr_aad = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); + }, + [&] { + is_encrypt = fuzzed_data_provider.ConsumeBool(); + }); } } diff --git a/src/test/fuzz/crypto_common.cpp b/src/test/fuzz/crypto_common.cpp index 7ccb125216..8e07dfedb9 100644 --- a/src/test/fuzz/crypto_common.cpp +++ b/src/test/fuzz/crypto_common.cpp @@ -13,7 +13,7 @@ #include <cstring> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_common) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const uint16_t random_u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>(); diff --git a/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp b/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp index e0a4e90c10..8cb9c55283 100644 --- a/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp +++ b/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp @@ -11,7 +11,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_hkdf_hmac_sha256_l32) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; diff --git a/src/test/fuzz/crypto_poly1305.cpp b/src/test/fuzz/crypto_poly1305.cpp index 5681e6a693..ac555ed68c 100644 --- a/src/test/fuzz/crypto_poly1305.cpp +++ b/src/test/fuzz/crypto_poly1305.cpp @@ -10,7 +10,7 @@ #include <cstdint> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(crypto_poly1305) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; diff --git a/src/test/fuzz/cuckoocache.cpp b/src/test/fuzz/cuckoocache.cpp index 5b45aa79d8..dc20dc3f62 100644 --- a/src/test/fuzz/cuckoocache.cpp +++ b/src/test/fuzz/cuckoocache.cpp @@ -26,7 +26,7 @@ struct RandomHasher { }; } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(cuckoocache) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); fuzzed_data_provider_ptr = &fuzzed_data_provider; diff --git a/src/test/fuzz/danger_link_all.sh b/src/test/fuzz/danger_link_all.sh new file mode 100755 index 0000000000..2ddd00c658 --- /dev/null +++ b/src/test/fuzz/danger_link_all.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +set -e + +ROOT_DIR="$(git rev-parse --show-toplevel)" + +# Run only once (break make recursion) +if [ -d "${ROOT_DIR}/lock_fuzz_link_all" ]; then + exit +fi +mkdir "${ROOT_DIR}/lock_fuzz_link_all" + +echo "Linking each fuzz target separately." +for FUZZING_HARNESS in $(PRINT_ALL_FUZZ_TARGETS_AND_ABORT=1 "${ROOT_DIR}/src/test/fuzz/fuzz" | sort -u); do + echo "Building src/test/fuzz/${FUZZING_HARNESS} ..." + git checkout -- "${ROOT_DIR}/src/test/fuzz/fuzz.cpp" + sed -i "s/std::getenv(\"FUZZ\")/\"${FUZZING_HARNESS}\"/g" "${ROOT_DIR}/src/test/fuzz/fuzz.cpp" + make + mv "${ROOT_DIR}/src/test/fuzz/fuzz" "${ROOT_DIR}/src/test/fuzz/${FUZZING_HARNESS}" +done +git checkout -- "${ROOT_DIR}/src/test/fuzz/fuzz.cpp" +rmdir "${ROOT_DIR}/lock_fuzz_link_all" +echo "Successfully built all fuzz targets." diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp new file mode 100644 index 0000000000..f3b6e6af04 --- /dev/null +++ b/src/test/fuzz/data_stream.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <addrman.h> +#include <net.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cstdint> +#include <vector> + +void initialize_data_stream_addr_man() +{ + static const auto testing_setup = MakeFuzzingContext<>(); +} + +FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider); + CAddrMan addr_man; + CAddrDB::Read(addr_man, data_stream); +} diff --git a/src/test/fuzz/decode_tx.cpp b/src/test/fuzz/decode_tx.cpp index a2b18c0365..57431e65ba 100644 --- a/src/test/fuzz/decode_tx.cpp +++ b/src/test/fuzz/decode_tx.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -12,7 +12,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(decode_tx) { const std::string tx_hex = HexStr(buffer); CMutableTransaction mtx; diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 7b57a2c1e2..0d1921f285 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -8,14 +8,14 @@ #include <test/fuzz/fuzz.h> #include <util/memory.h> -void initialize() +void initialize_descriptor_parse() { static const ECCVerifyHandle verify_handle; ECC_Start(); SelectParams(CBaseChainParams::MAIN); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(descriptor_parse, initialize_descriptor_parse) { const std::string descriptor(buffer.begin(), buffer.end()); FlatSigningProvider signing_provider; diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp index 8ca5366c8a..74dec6475e 100644 --- a/src/test/fuzz/deserialize.cpp +++ b/src/test/fuzz/deserialize.cpp @@ -34,12 +34,21 @@ #include <test/fuzz/fuzz.h> -void initialize() +void initialize_deserialize() { // Fuzzers using pubkey must hold an ECCVerifyHandle. static const ECCVerifyHandle verify_handle; } +#define FUZZ_TARGET_DESERIALIZE(name, code) \ + FUZZ_TARGET_INIT(name, initialize_deserialize) \ + { \ + try { \ + code \ + } catch (const invalid_fuzzing_input_exception&) { \ + } \ + } + namespace { struct invalid_fuzzing_input_exception : public std::exception { @@ -92,44 +101,51 @@ void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) -{ - try { -#if BLOCK_FILTER_DESERIALIZE +FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, { BlockFilter block_filter; DeserializeFromFuzzingInput(buffer, block_filter); -#elif ADDR_INFO_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(addr_info_deserialize, { CAddrInfo addr_info; DeserializeFromFuzzingInput(buffer, addr_info); -#elif BLOCK_FILE_INFO_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, { CBlockFileInfo block_file_info; DeserializeFromFuzzingInput(buffer, block_file_info); -#elif BLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(block_header_and_short_txids_deserialize, { CBlockHeaderAndShortTxIDs block_header_and_short_txids; DeserializeFromFuzzingInput(buffer, block_header_and_short_txids); -#elif FEE_RATE_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(fee_rate_deserialize, { CFeeRate fee_rate; DeserializeFromFuzzingInput(buffer, fee_rate); AssertEqualAfterSerializeDeserialize(fee_rate); -#elif MERKLE_BLOCK_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(merkle_block_deserialize, { CMerkleBlock merkle_block; DeserializeFromFuzzingInput(buffer, merkle_block); -#elif OUT_POINT_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(out_point_deserialize, { COutPoint out_point; DeserializeFromFuzzingInput(buffer, out_point); AssertEqualAfterSerializeDeserialize(out_point); -#elif PARTIAL_MERKLE_TREE_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(partial_merkle_tree_deserialize, { CPartialMerkleTree partial_merkle_tree; DeserializeFromFuzzingInput(buffer, partial_merkle_tree); -#elif PUB_KEY_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, { CPubKey pub_key; DeserializeFromFuzzingInput(buffer, pub_key); // TODO: The following equivalence should hold for CPubKey? Fix. // AssertEqualAfterSerializeDeserialize(pub_key); -#elif SCRIPT_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(script_deserialize, { CScript script; DeserializeFromFuzzingInput(buffer, script); -#elif SUB_NET_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(sub_net_deserialize, { CSubNet sub_net_1; DeserializeFromFuzzingInput(buffer, sub_net_1, INIT_PROTO_VERSION); AssertEqualAfterSerializeDeserialize(sub_net_1, INIT_PROTO_VERSION); @@ -139,67 +155,85 @@ void test_one_input(const std::vector<uint8_t>& buffer) CSubNet sub_net_3; DeserializeFromFuzzingInput(buffer, sub_net_3); AssertEqualAfterSerializeDeserialize(sub_net_3, INIT_PROTO_VERSION | ADDRV2_FORMAT); -#elif TX_IN_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(tx_in_deserialize, { CTxIn tx_in; DeserializeFromFuzzingInput(buffer, tx_in); AssertEqualAfterSerializeDeserialize(tx_in); -#elif FLAT_FILE_POS_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(flat_file_pos_deserialize, { FlatFilePos flat_file_pos; DeserializeFromFuzzingInput(buffer, flat_file_pos); AssertEqualAfterSerializeDeserialize(flat_file_pos); -#elif KEY_ORIGIN_INFO_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(key_origin_info_deserialize, { KeyOriginInfo key_origin_info; DeserializeFromFuzzingInput(buffer, key_origin_info); AssertEqualAfterSerializeDeserialize(key_origin_info); -#elif PARTIALLY_SIGNED_TRANSACTION_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(partially_signed_transaction_deserialize, { PartiallySignedTransaction partially_signed_transaction; DeserializeFromFuzzingInput(buffer, partially_signed_transaction); -#elif PREFILLED_TRANSACTION_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(prefilled_transaction_deserialize, { PrefilledTransaction prefilled_transaction; DeserializeFromFuzzingInput(buffer, prefilled_transaction); -#elif PSBT_INPUT_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(psbt_input_deserialize, { PSBTInput psbt_input; DeserializeFromFuzzingInput(buffer, psbt_input); -#elif PSBT_OUTPUT_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(psbt_output_deserialize, { PSBTOutput psbt_output; DeserializeFromFuzzingInput(buffer, psbt_output); -#elif BLOCK_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(block_deserialize, { CBlock block; DeserializeFromFuzzingInput(buffer, block); -#elif BLOCKLOCATOR_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(blocklocator_deserialize, { CBlockLocator bl; DeserializeFromFuzzingInput(buffer, bl); -#elif BLOCKMERKLEROOT +}) +FUZZ_TARGET_DESERIALIZE(blockmerkleroot, { CBlock block; DeserializeFromFuzzingInput(buffer, block); bool mutated; BlockMerkleRoot(block, &mutated); -#elif ADDRMAN_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(addrman_deserialize, { CAddrMan am; DeserializeFromFuzzingInput(buffer, am); -#elif BLOCKHEADER_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, { CBlockHeader bh; DeserializeFromFuzzingInput(buffer, bh); -#elif BANENTRY_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(banentry_deserialize, { CBanEntry be; DeserializeFromFuzzingInput(buffer, be); -#elif TXUNDO_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(txundo_deserialize, { CTxUndo tu; DeserializeFromFuzzingInput(buffer, tu); -#elif BLOCKUNDO_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(blockundo_deserialize, { CBlockUndo bu; DeserializeFromFuzzingInput(buffer, bu); -#elif COINS_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(coins_deserialize, { Coin coin; DeserializeFromFuzzingInput(buffer, coin); -#elif NETADDR_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(netaddr_deserialize, { CNetAddr na; DeserializeFromFuzzingInput(buffer, na); if (na.IsAddrV1Compatible()) { AssertEqualAfterSerializeDeserialize(na); } AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT); -#elif SERVICE_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(service_deserialize, { CService s; DeserializeFromFuzzingInput(buffer, s); if (s.IsAddrV1Compatible()) { @@ -213,50 +247,56 @@ void test_one_input(const std::vector<uint8_t>& buffer) CService s2; DeserializeFromFuzzingInput(buffer, s2, INIT_PROTO_VERSION | ADDRV2_FORMAT); AssertEqualAfterSerializeDeserialize(s2, INIT_PROTO_VERSION | ADDRV2_FORMAT); -#elif MESSAGEHEADER_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, { CMessageHeader mh; DeserializeFromFuzzingInput(buffer, mh); (void)mh.IsCommandValid(); -#elif ADDRESS_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(address_deserialize, { CAddress a; DeserializeFromFuzzingInput(buffer, a); -#elif INV_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(inv_deserialize, { CInv i; DeserializeFromFuzzingInput(buffer, i); -#elif BLOOMFILTER_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(bloomfilter_deserialize, { CBloomFilter bf; DeserializeFromFuzzingInput(buffer, bf); -#elif DISKBLOCKINDEX_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(diskblockindex_deserialize, { CDiskBlockIndex dbi; DeserializeFromFuzzingInput(buffer, dbi); -#elif TXOUTCOMPRESSOR_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(txoutcompressor_deserialize, { CTxOut to; auto toc = Using<TxOutCompression>(to); DeserializeFromFuzzingInput(buffer, toc); -#elif BLOCKTRANSACTIONS_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(blocktransactions_deserialize, { BlockTransactions bt; DeserializeFromFuzzingInput(buffer, bt); -#elif BLOCKTRANSACTIONSREQUEST_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(blocktransactionsrequest_deserialize, { BlockTransactionsRequest btr; DeserializeFromFuzzingInput(buffer, btr); -#elif SNAPSHOTMETADATA_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(snapshotmetadata_deserialize, { SnapshotMetadata snapshot_metadata; DeserializeFromFuzzingInput(buffer, snapshot_metadata); -#elif UINT160_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(uint160_deserialize, { uint160 u160; DeserializeFromFuzzingInput(buffer, u160); AssertEqualAfterSerializeDeserialize(u160); -#elif UINT256_DESERIALIZE +}) +FUZZ_TARGET_DESERIALIZE(uint256_deserialize, { uint256 u256; DeserializeFromFuzzingInput(buffer, u256); AssertEqualAfterSerializeDeserialize(u256); -#else -#error Need at least one fuzz target to compile -#endif +}) // Classes intentionally not covered in this file since their deserialization code is // fuzzed elsewhere: // * Deserialization of CTxOut is fuzzed in test/fuzz/tx_out.cpp // * Deserialization of CMutableTransaction is fuzzed in src/test/fuzz/transaction.cpp - } catch (const invalid_fuzzing_input_exception&) { - } -} diff --git a/src/test/fuzz/eval_script.cpp b/src/test/fuzz/eval_script.cpp index c556599db3..635288fc36 100644 --- a/src/test/fuzz/eval_script.cpp +++ b/src/test/fuzz/eval_script.cpp @@ -10,12 +10,12 @@ #include <limits> -void initialize() +void initialize_eval_script() { static const ECCVerifyHandle verify_handle; } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(eval_script, initialize_eval_script) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); diff --git a/src/test/fuzz/fee_rate.cpp b/src/test/fuzz/fee_rate.cpp index f3d44d9f93..2955213635 100644 --- a/src/test/fuzz/fee_rate.cpp +++ b/src/test/fuzz/fee_rate.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(fee_rate) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CAmount satoshis_per_k = ConsumeMoney(fuzzed_data_provider); diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp index ce8700befa..61c7681bf9 100644 --- a/src/test/fuzz/fees.cpp +++ b/src/test/fuzz/fees.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(fees) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CFeeRate minimal_incremental_fee{ConsumeMoney(fuzzed_data_provider)}; diff --git a/src/test/fuzz/flatfile.cpp b/src/test/fuzz/flatfile.cpp index 95dabb8bab..d142e374b1 100644 --- a/src/test/fuzz/flatfile.cpp +++ b/src/test/fuzz/flatfile.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(flatfile) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); std::optional<FlatFilePos> flat_file_pos = ConsumeDeserializable<FlatFilePos>(fuzzed_data_provider); diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp index a24bae5b35..d18a87d177 100644 --- a/src/test/fuzz/float.cpp +++ b/src/test/fuzz/float.cpp @@ -12,7 +12,7 @@ #include <cassert> #include <cstdint> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(float) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index 753cfffdcb..fd87667755 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -5,6 +5,7 @@ #include <test/fuzz/fuzz.h> #include <test/util/setup_common.h> +#include <util/check.h> #include <cstdint> #include <unistd.h> @@ -12,6 +13,36 @@ const std::function<void(const std::string&)> G_TEST_LOG_FUN{}; +std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize>>& FuzzTargets() +{ + static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize>> g_fuzz_targets; + return g_fuzz_targets; +} + +void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init) +{ + const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init)); + Assert(it_ins.second); +} + +static TypeTestOneInput* g_test_one_input{nullptr}; + +void initialize() +{ + if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) { + for (const auto& t : FuzzTargets()) { + std::cout << t.first << std::endl; + } + Assert(false); + } + std::string_view fuzz_target{Assert(std::getenv("FUZZ"))}; + const auto it = FuzzTargets().find(fuzz_target); + Assert(it != FuzzTargets().end()); + Assert(!g_test_one_input); + g_test_one_input = &std::get<0>(it->second); + std::get<1>(it->second)(); +} + #if defined(PROVIDE_MAIN_FUNCTION) static bool read_stdin(std::vector<uint8_t>& data) { @@ -24,14 +55,10 @@ static bool read_stdin(std::vector<uint8_t>& data) } #endif -// Default initialization: Override using a non-weak initialize(). -__attribute__((weak)) void initialize() -{ -} - // This function is used by libFuzzer extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static const auto& test_one_input = *Assert(g_test_one_input); const std::vector<uint8_t> input(data, data + size); test_one_input(input); return 0; @@ -48,6 +75,7 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) __attribute__((weak)) int main(int argc, char** argv) { initialize(); + static const auto& test_one_input = *Assert(g_test_one_input); #ifdef __AFL_INIT // Enable AFL deferred forkserver mode. Requires compilation using // afl-clang-fast++. See fuzzing.md for details. diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h index 3be202b16e..52841e069a 100644 --- a/src/test/fuzz/fuzz.h +++ b/src/test/fuzz/fuzz.h @@ -1,14 +1,33 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_TEST_FUZZ_FUZZ_H #define BITCOIN_TEST_FUZZ_FUZZ_H -#include <stdint.h> +#include <cstdint> +#include <functional> +#include <string_view> #include <vector> -void initialize(); -void test_one_input(const std::vector<uint8_t>& buffer); +using TypeTestOneInput = std::function<void(const std::vector<uint8_t>&)>; +using TypeInitialize = std::function<void()>; + +void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init); + +inline void FuzzFrameworkEmptyFun() {} + +#define FUZZ_TARGET(name) \ + FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyFun) + +#define FUZZ_TARGET_INIT(name, init_fun) \ + void name##_fuzz_target(const std::vector<uint8_t>&); \ + struct name##_Before_Main { \ + name##_Before_Main() \ + { \ + FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun); \ + } \ + } const static g_##name##_before_main; \ + void name##_fuzz_target(const std::vector<uint8_t>& buffer) #endif // BITCOIN_TEST_FUZZ_FUZZ_H diff --git a/src/test/fuzz/golomb_rice.cpp b/src/test/fuzz/golomb_rice.cpp index a9f450b0c4..c99bf940c7 100644 --- a/src/test/fuzz/golomb_rice.cpp +++ b/src/test/fuzz/golomb_rice.cpp @@ -54,7 +54,7 @@ std::vector<uint64_t> BuildHashedSet(const std::unordered_set<std::vector<uint8_ } } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(golomb_rice) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); std::vector<uint8_t> golomb_rice_data; diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp index 6a8699fd0f..cc1bc1c8cf 100644 --- a/src/test/fuzz/hex.cpp +++ b/src/test/fuzz/hex.cpp @@ -16,12 +16,12 @@ #include <string> #include <vector> -void initialize() +void initialize_hex() { static const ECCVerifyHandle verify_handle; } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(hex, initialize_hex) { const std::string random_hex_string(buffer.begin(), buffer.end()); const std::vector<unsigned char> data = ParseHex(random_hex_string); diff --git a/src/test/fuzz/http_request.cpp b/src/test/fuzz/http_request.cpp index 36d44e361f..e3b62032bc 100644 --- a/src/test/fuzz/http_request.cpp +++ b/src/test/fuzz/http_request.cpp @@ -39,7 +39,7 @@ extern "C" int evhttp_parse_headers_(struct evhttp_request*, struct evbuffer*); std::string RequestMethodString(HTTPRequest::RequestMethod m); -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(http_request) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; evhttp_request* evreq = evhttp_request_new(nullptr, nullptr); diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index 35d6804d4f..ac83d91ea0 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -40,12 +40,12 @@ #include <set> #include <vector> -void initialize() +void initialize_integer() { SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(integer, initialize_integer) { if (buffer.size() < sizeof(uint256) + sizeof(uint160)) { return; diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp index 955b954700..aa8f826e4a 100644 --- a/src/test/fuzz/key.cpp +++ b/src/test/fuzz/key.cpp @@ -26,14 +26,14 @@ #include <string> #include <vector> -void initialize() +void initialize_key() { static const ECCVerifyHandle ecc_verify_handle; ECC_Start(); SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(key, initialize_key) { const CKey key = [&] { CKey k; diff --git a/src/test/fuzz/key_io.cpp b/src/test/fuzz/key_io.cpp index 62aefb650d..665ca01fa1 100644 --- a/src/test/fuzz/key_io.cpp +++ b/src/test/fuzz/key_io.cpp @@ -14,14 +14,14 @@ #include <string> #include <vector> -void initialize() +void initialize_key_io() { static const ECCVerifyHandle verify_handle; ECC_Start(); SelectParams(CBaseChainParams::MAIN); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(key_io, initialize_key_io) { const std::string random_string(buffer.begin(), buffer.end()); diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp index 82cbc00a3a..fa4024fc38 100644 --- a/src/test/fuzz/kitchen_sink.cpp +++ b/src/test/fuzz/kitchen_sink.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 <merkleblock.h> +#include <policy/fees.h> #include <rpc/util.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> @@ -9,18 +11,49 @@ #include <util/error.h> #include <util/translation.h> +#include <array> #include <cstdint> #include <vector> +namespace { +constexpr TransactionError ALL_TRANSACTION_ERROR[] = { + TransactionError::OK, + TransactionError::MISSING_INPUTS, + TransactionError::ALREADY_IN_CHAIN, + TransactionError::P2P_DISABLED, + TransactionError::MEMPOOL_REJECTED, + TransactionError::MEMPOOL_ERROR, + TransactionError::INVALID_PSBT, + TransactionError::PSBT_MISMATCH, + TransactionError::SIGHASH_MISMATCH, + TransactionError::MAX_FEE_EXCEEDED, +}; +}; // namespace + // The fuzzing kitchen sink: Fuzzing harness for functions that need to be // fuzzed but a.) don't belong in any existing fuzzing harness file, and // b.) are not important enough to warrant their own fuzzing harness file. -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(kitchen_sink) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - const TransactionError transaction_error = fuzzed_data_provider.PickValueInArray<TransactionError>({TransactionError::OK, TransactionError::MISSING_INPUTS, TransactionError::ALREADY_IN_CHAIN, TransactionError::P2P_DISABLED, TransactionError::MEMPOOL_REJECTED, TransactionError::MEMPOOL_ERROR, TransactionError::INVALID_PSBT, TransactionError::PSBT_MISMATCH, TransactionError::SIGHASH_MISMATCH, TransactionError::MAX_FEE_EXCEEDED}); + const TransactionError transaction_error = fuzzed_data_provider.PickValueInArray(ALL_TRANSACTION_ERROR); (void)JSONRPCTransactionError(transaction_error); (void)RPCErrorFromTransactionError(transaction_error); (void)TransactionErrorString(transaction_error); + + (void)StringForFeeEstimateHorizon(fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS)); + + const OutputType output_type = fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES); + const std::string& output_type_string = FormatOutputType(output_type); + OutputType output_type_parsed; + const bool parsed = ParseOutputType(output_type_string, output_type_parsed); + assert(parsed); + assert(output_type == output_type_parsed); + (void)ParseOutputType(fuzzed_data_provider.ConsumeRandomLengthString(64), output_type_parsed); + + const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider); + const std::vector<bool> bits = BytesToBits(bytes); + const std::vector<uint8_t> bytes_decoded = BitsToBytes(bits); + assert(bytes == bytes_decoded); } diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index d9de9d9866..95597bf082 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -13,12 +13,12 @@ #include <cstdint> #include <vector> -void initialize() +void initialize_load_external_block_file() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeFuzzingContext<const TestingSetup>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); @@ -27,5 +27,5 @@ void test_one_input(const std::vector<uint8_t>& buffer) return; } FlatFilePos flat_file_pos; - LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr); + ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr); } diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp index 2b181c6da1..5b1acae57b 100644 --- a/src/test/fuzz/locale.cpp +++ b/src/test/fuzz/locale.cpp @@ -35,7 +35,7 @@ bool IsAvailableLocale(const std::string& locale_identifier) } } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(locale) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string locale_identifier = ConsumeLocaleIdentifier(fuzzed_data_provider); diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp index 4710e75757..23e0baa564 100644 --- a/src/test/fuzz/merkleblock.cpp +++ b/src/test/fuzz/merkleblock.cpp @@ -13,37 +13,35 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(merkleblock) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CPartialMerkleTree partial_merkle_tree; - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1)) { - case 0: { - const std::optional<CPartialMerkleTree> opt_partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider); - if (opt_partial_merkle_tree) { - partial_merkle_tree = *opt_partial_merkle_tree; - } - break; - } - case 1: { - CMerkleBlock merkle_block; - const std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider); - CBloomFilter bloom_filter; - std::set<uint256> txids; - if (opt_block && !opt_block->vtx.empty()) { - if (fuzzed_data_provider.ConsumeBool()) { - merkle_block = CMerkleBlock{*opt_block, bloom_filter}; - } else if (fuzzed_data_provider.ConsumeBool()) { - while (fuzzed_data_provider.ConsumeBool()) { - txids.insert(ConsumeUInt256(fuzzed_data_provider)); + CallOneOf( + fuzzed_data_provider, + [&] { + const std::optional<CPartialMerkleTree> opt_partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider); + if (opt_partial_merkle_tree) { + partial_merkle_tree = *opt_partial_merkle_tree; + } + }, + [&] { + CMerkleBlock merkle_block; + const std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider); + CBloomFilter bloom_filter; + std::set<uint256> txids; + if (opt_block && !opt_block->vtx.empty()) { + if (fuzzed_data_provider.ConsumeBool()) { + merkle_block = CMerkleBlock{*opt_block, bloom_filter}; + } else if (fuzzed_data_provider.ConsumeBool()) { + while (fuzzed_data_provider.ConsumeBool()) { + txids.insert(ConsumeUInt256(fuzzed_data_provider)); + } + merkle_block = CMerkleBlock{*opt_block, txids}; } - merkle_block = CMerkleBlock{*opt_block, txids}; } - } - partial_merkle_tree = merkle_block.txn; - break; - } - } + partial_merkle_tree = merkle_block.txn; + }); (void)partial_merkle_tree.GetNumTransactions(); std::vector<uint256> matches; std::vector<unsigned int> indices; diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp index fa0322a391..06cd0afe2a 100644 --- a/src/test/fuzz/message.cpp +++ b/src/test/fuzz/message.cpp @@ -16,14 +16,14 @@ #include <string> #include <vector> -void initialize() +void initialize_message() { static const ECCVerifyHandle ecc_verify_handle; ECC_Start(); SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(message, initialize_message) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string random_message = fuzzed_data_provider.ConsumeRandomLengthString(1024); diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp new file mode 100644 index 0000000000..8f843ca773 --- /dev/null +++ b/src/test/fuzz/muhash.cpp @@ -0,0 +1,53 @@ +// 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/muhash.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <vector> + +FUZZ_TARGET(muhash) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + std::vector<uint8_t> data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider); + if (data.empty()) { + data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); + } + if (data2.empty()) { + data2.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); + } + + data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider); + + MuHash3072 muhash; + + // Test that MuHash result is consistent independent of order of operations + muhash.Insert(data); + muhash.Insert(data2); + + uint256 out; + muhash.Finalize(out); + + muhash = MuHash3072(); + muhash.Insert(data2); + muhash.Insert(data); + + uint256 out2; + muhash.Finalize(out2); + + assert(out == out2); + + // Test that removing all added elements brings the object back to it's initial state + muhash /= muhash; + muhash.Finalize(out); + + MuHash3072 muhash2; + muhash2.Finalize(out2); + + assert(out == out2); +} diff --git a/src/test/fuzz/multiplication_overflow.cpp b/src/test/fuzz/multiplication_overflow.cpp index a4b158c18b..0f054529a6 100644 --- a/src/test/fuzz/multiplication_overflow.cpp +++ b/src/test/fuzz/multiplication_overflow.cpp @@ -14,7 +14,7 @@ #if __has_builtin(__builtin_mul_overflow) #define HAVE_BUILTIN_MUL_OVERFLOW #endif -#elif defined(__GNUC__) && (__GNUC__ >= 5) +#elif defined(__GNUC__) #define HAVE_BUILTIN_MUL_OVERFLOW #endif @@ -40,7 +40,7 @@ void TestMultiplicationOverflow(FuzzedDataProvider& fuzzed_data_provider) } } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(multiplication_overflow) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); TestMultiplicationOverflow<int64_t>(fuzzed_data_provider); diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index a0c8b7aac5..21dca4eb05 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -13,125 +13,93 @@ #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <test/util/net.h> #include <test/util/setup_common.h> #include <cstdint> #include <string> #include <vector> -void initialize() +void initialize_net() { - static const BasicTestingSetup basic_testing_setup; + static const auto testing_setup = MakeFuzzingContext<>(CBaseChainParams::MAIN); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(net, initialize_net) { 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()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + CNode node{ConsumeNode(fuzzed_data_provider)}; + node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>()); 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 = ConsumeRandomLengthBitVector(fuzzed_data_provider); - 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(b, complete); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + node.CloseSocketDisconnect(); + }, + [&] { + node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32)); + }, + [&] { + const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); + if (!SanityCheckASMap(asmap)) { + return; + } + CNodeStats stats; + node.copyStats(stats, asmap); + }, + [&] { + const CNode* add_ref_node = node.AddRef(); + assert(add_ref_node == &node); + }, + [&] { + if (node.GetRefCount() > 0) { + node.Release(); + } + }, + [&] { + if (node.m_addr_known == nullptr) { + return; + } + const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider); + if (!addr_opt) { + return; + } + node.AddAddressKnown(*addr_opt); + }, + [&] { + if (node.m_addr_known == nullptr) { + return; + } + const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider); + if (!addr_opt) { + return; + } + FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; + node.PushAddress(*addr_opt, fast_random_context); + }, + [&] { + const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider); + if (!inv_opt) { + return; + } + node.AddKnownTx(inv_opt->hash); + }, + [&] { + node.PushTxInventory(ConsumeUInt256(fuzzed_data_provider)); + }, + [&] { + const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (!service_opt) { + return; + } + node.SetAddrLocal(*service_opt); + }, + [&] { + const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + bool complete; + node.ReceiveMsgBytes(b, complete); + }); } (void)node.GetAddrLocal(); @@ -139,15 +107,12 @@ void test_one_input(const std::vector<uint8_t>& buffer) (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>()); + const NetPermissionFlags net_permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); (void)node.HasPermission(net_permission_flags); (void)node.ConnectedThroughNetwork(); } diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp index 8a674ac1e9..544a33047b 100644 --- a/src/test/fuzz/net_permissions.cpp +++ b/src/test/fuzz/net_permissions.cpp @@ -13,22 +13,11 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(net_permissions) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(32); - 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_ADDR, - NetPermissionFlags::PF_ISIMPLICIT, - NetPermissionFlags::PF_ALL, - }) : - static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>()); + const NetPermissionFlags net_permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); NetWhitebindPermissions net_whitebind_permissions; bilingual_str error_net_whitebind_permissions; diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp index 8252f38726..6e9bb47ff6 100644 --- a/src/test/fuzz/netaddress.cpp +++ b/src/test/fuzz/netaddress.cpp @@ -12,7 +12,7 @@ #include <netinet/in.h> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(netaddress) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp new file mode 100644 index 0000000000..aaebe83c0a --- /dev/null +++ b/src/test/fuzz/node_eviction.cpp @@ -0,0 +1,44 @@ +// 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 <net.h> +#include <optional.h> +#include <protocol.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <optional> +#include <vector> + +FUZZ_TARGET(node_eviction) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + std::vector<NodeEvictionCandidate> eviction_candidates; + while (fuzzed_data_provider.ConsumeBool()) { + eviction_candidates.push_back({ + fuzzed_data_provider.ConsumeIntegral<NodeId>(), + fuzzed_data_provider.ConsumeIntegral<int64_t>(), + fuzzed_data_provider.ConsumeIntegral<int64_t>(), + fuzzed_data_provider.ConsumeIntegral<int64_t>(), + fuzzed_data_provider.ConsumeIntegral<int64_t>(), + fuzzed_data_provider.ConsumeBool(), + fuzzed_data_provider.ConsumeBool(), + fuzzed_data_provider.ConsumeBool(), + fuzzed_data_provider.ConsumeIntegral<uint64_t>(), + fuzzed_data_provider.ConsumeBool(), + fuzzed_data_provider.ConsumeBool(), + }); + } + // Make a copy since eviction_candidates may be in some valid but otherwise + // indeterminate state after the SelectNodeToEvict(&&) call. + const std::vector<NodeEvictionCandidate> eviction_candidates_copy = eviction_candidates; + const Optional<NodeId> node_to_evict = SelectNodeToEvict(std::move(eviction_candidates)); + if (node_to_evict) { + assert(std::any_of(eviction_candidates_copy.begin(), eviction_candidates_copy.end(), [&node_to_evict](const NodeEvictionCandidate& eviction_candidate) { return *node_to_evict == eviction_candidate.id; })); + } +} diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp index 7a6236efac..163f1b839e 100644 --- a/src/test/fuzz/p2p_transport_deserializer.cpp +++ b/src/test/fuzz/p2p_transport_deserializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -12,12 +12,12 @@ #include <limits> #include <vector> -void initialize() +void initialize_p2p_transport_deserializer() { SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(p2p_transport_deserializer, initialize_p2p_transport_deserializer) { // Construct deserializer, with a dummy NodeId V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION}; diff --git a/src/test/fuzz/parse_hd_keypath.cpp b/src/test/fuzz/parse_hd_keypath.cpp index f668ca8c48..411b70230a 100644 --- a/src/test/fuzz/parse_hd_keypath.cpp +++ b/src/test/fuzz/parse_hd_keypath.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,7 +10,7 @@ #include <cstdint> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(parse_hd_keypath) { const std::string keypath_str(buffer.begin(), buffer.end()); std::vector<uint32_t> keypath; diff --git a/src/test/fuzz/parse_iso8601.cpp b/src/test/fuzz/parse_iso8601.cpp index c86f8a853e..dcb24ac127 100644 --- a/src/test/fuzz/parse_iso8601.cpp +++ b/src/test/fuzz/parse_iso8601.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -11,7 +11,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(parse_iso8601) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp index 59f89dc9fb..ddd2bcfba3 100644 --- a/src/test/fuzz/parse_numbers.cpp +++ b/src/test/fuzz/parse_numbers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,7 +8,7 @@ #include <string> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(parse_numbers) { const std::string random_string(buffer.begin(), buffer.end()); diff --git a/src/test/fuzz/parse_script.cpp b/src/test/fuzz/parse_script.cpp index 21ac1aecf3..060ad3db12 100644 --- a/src/test/fuzz/parse_script.cpp +++ b/src/test/fuzz/parse_script.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -6,7 +6,7 @@ #include <script/script.h> #include <test/fuzz/fuzz.h> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(parse_script) { const std::string script_string(buffer.begin(), buffer.end()); try { diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp index a269378607..afe382ba21 100644 --- a/src/test/fuzz/parse_univalue.cpp +++ b/src/test/fuzz/parse_univalue.cpp @@ -12,13 +12,13 @@ #include <limits> #include <string> -void initialize() +void initialize_parse_univalue() { static const ECCVerifyHandle verify_handle; SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(parse_univalue, initialize_parse_univalue) { const std::string random_string(buffer.begin(), buffer.end()); bool valid = true; diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index 6c94a47f3c..fff893fb3f 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -14,62 +14,58 @@ #include <string> #include <vector> -void initialize() +void initialize_policy_estimator() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeFuzzingContext<>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(policy_estimator, initialize_policy_estimator) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CBlockPolicyEstimator block_policy_estimator; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) { - case 0: { - const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); - if (!mtx) { - break; - } - const CTransaction tx{*mtx}; - block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool()); - if (fuzzed_data_provider.ConsumeBool()) { - (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool()); - } - break; - } - case 1: { - std::vector<CTxMemPoolEntry> mempool_entries; - while (fuzzed_data_provider.ConsumeBool()) { + CallOneOf( + fuzzed_data_provider, + [&] { const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); if (!mtx) { - break; + return; } const CTransaction tx{*mtx}; - mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx)); - } - std::vector<const CTxMemPoolEntry*> ptrs; - ptrs.reserve(mempool_entries.size()); - for (const CTxMemPoolEntry& mempool_entry : mempool_entries) { - ptrs.push_back(&mempool_entry); - } - block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral<unsigned int>(), ptrs); - break; - } - case 2: { - (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool()); - break; - } - case 3: { - block_policy_estimator.FlushUnconfirmed(); - break; - } - } + block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool()); + if (fuzzed_data_provider.ConsumeBool()) { + (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool()); + } + }, + [&] { + std::vector<CTxMemPoolEntry> mempool_entries; + while (fuzzed_data_provider.ConsumeBool()) { + const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); + if (!mtx) { + break; + } + const CTransaction tx{*mtx}; + mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx)); + } + std::vector<const CTxMemPoolEntry*> ptrs; + ptrs.reserve(mempool_entries.size()); + for (const CTxMemPoolEntry& mempool_entry : mempool_entries) { + ptrs.push_back(&mempool_entry); + } + block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral<unsigned int>(), ptrs); + }, + [&] { + (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool()); + }, + [&] { + block_policy_estimator.FlushUnconfirmed(); + }); (void)block_policy_estimator.estimateFee(fuzzed_data_provider.ConsumeIntegral<int>()); EstimationResult result; - (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}), fuzzed_data_provider.ConsumeBool() ? &result : nullptr); + (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS), fuzzed_data_provider.ConsumeBool() ? &result : nullptr); FeeCalculation fee_calculation; (void)block_policy_estimator.estimateSmartFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr, fuzzed_data_provider.ConsumeBool()); - (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE})); + (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS)); } { FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp index 0edcf201c7..73242870a0 100644 --- a/src/test/fuzz/policy_estimator_io.cpp +++ b/src/test/fuzz/policy_estimator_io.cpp @@ -10,12 +10,12 @@ #include <cstdint> #include <vector> -void initialize() +void initialize_policy_estimator_io() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeFuzzingContext<>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(policy_estimator_io, initialize_policy_estimator_io) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp index b7fc72373d..c4348495bf 100644 --- a/src/test/fuzz/pow.cpp +++ b/src/test/fuzz/pow.cpp @@ -15,12 +15,12 @@ #include <string> #include <vector> -void initialize() +void initialize_pow() { SelectParams(CBaseChainParams::MAIN); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(pow, initialize_pow) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const Consensus::Params& consensus_params = Params().GetConsensus(); @@ -43,7 +43,10 @@ void test_one_input(const std::vector<uint8_t>& buffer) current_block.nHeight = current_height; } if (fuzzed_data_provider.ConsumeBool()) { - current_block.nTime = fixed_time + current_height * consensus_params.nPowTargetSpacing; + const uint32_t seconds = current_height * consensus_params.nPowTargetSpacing; + if (!AdditionOverflow(fixed_time, seconds)) { + current_block.nTime = fixed_time + seconds; + } } if (fuzzed_data_provider.ConsumeBool()) { current_block.nBits = fixed_bits; diff --git a/src/test/fuzz/prevector.cpp b/src/test/fuzz/prevector.cpp index 626e187cbd..51956bbe9e 100644 --- a/src/test/fuzz/prevector.cpp +++ b/src/test/fuzz/prevector.cpp @@ -204,7 +204,7 @@ public: } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(prevector) { FuzzedDataProvider prov(buffer.data(), buffer.size()); prevector_tester<8, int> test; diff --git a/src/test/fuzz/primitives_transaction.cpp b/src/test/fuzz/primitives_transaction.cpp index 4a0f920f58..48815c8910 100644 --- a/src/test/fuzz/primitives_transaction.cpp +++ b/src/test/fuzz/primitives_transaction.cpp @@ -12,7 +12,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(primitives_transaction) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CScript script = ConsumeScript(fuzzed_data_provider); diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index 9390399878..e7cc0f5297 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -13,6 +13,7 @@ #include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> #include <test/util/mining.h> #include <test/util/net.h> #include <test/util/setup_common.h> @@ -32,59 +33,81 @@ #include <vector> namespace { - -#ifdef MESSAGE_TYPE -#define TO_STRING_(s) #s -#define TO_STRING(s) TO_STRING_(s) -const std::string LIMIT_TO_MESSAGE_TYPE{TO_STRING(MESSAGE_TYPE)}; -#else -const std::string LIMIT_TO_MESSAGE_TYPE; -#endif - const TestingSetup* g_setup; } // namespace -void initialize() +void initialize_process_message() { - static TestingSetup setup{ - CBaseChainParams::REGTEST, - { - "-nodebuglogfile", - }, - }; - g_setup = &setup; - + static const auto testing_setup = MakeFuzzingContext<const TestingSetup>(); + g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { MineBlock(g_setup->m_node, CScript() << OP_TRUE); } SyncWithValidationInterfaceQueue(); } -void test_one_input(const std::vector<uint8_t>& buffer) +void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO_MESSAGE_TYPE) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get(); TestChainState& chainstate = *(TestChainState*)&g_setup->m_node.chainman->ActiveChainstate(); + SetMockTime(1610000000); // any time to successfully reset ibd chainstate.ResetIbd(); + const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()}; if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) { return; } - const bool jump_out_of_ibd{fuzzed_data_provider.ConsumeBool()}; - if (jump_out_of_ibd) chainstate.JumpOutOfIbd(); - 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{}, ConnectionType::OUTBOUND_FULL_RELAY).release(); - p2p_node.fSuccessfullyConnected = true; - p2p_node.nVersion = PROTOCOL_VERSION; - p2p_node.SetCommonVersion(PROTOCOL_VERSION); + CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider).release(); + + const bool successfully_connected{true}; + p2p_node.fSuccessfullyConnected = successfully_connected; connman.AddTestNode(p2p_node); g_setup->m_node.peerman->InitializeNode(&p2p_node); + FillNode(fuzzed_data_provider, p2p_node, /* init_version */ successfully_connected); + + const auto mock_time = ConsumeTime(fuzzed_data_provider); + SetMockTime(mock_time); + + // fuzzed_data_provider is fully consumed after this call, don't use it + CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION}; try { 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&) { } + { + LOCK(p2p_node.cs_sendProcessing); + g_setup->m_node.peerman->SendMessages(&p2p_node); + } SyncWithValidationInterfaceQueue(); LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement g_setup->m_node.connman->StopNodes(); } + +FUZZ_TARGET_INIT(process_message, initialize_process_message) { fuzz_target(buffer, ""); } +FUZZ_TARGET_INIT(process_message_addr, initialize_process_message) { fuzz_target(buffer, "addr"); } +FUZZ_TARGET_INIT(process_message_block, initialize_process_message) { fuzz_target(buffer, "block"); } +FUZZ_TARGET_INIT(process_message_blocktxn, initialize_process_message) { fuzz_target(buffer, "blocktxn"); } +FUZZ_TARGET_INIT(process_message_cmpctblock, initialize_process_message) { fuzz_target(buffer, "cmpctblock"); } +FUZZ_TARGET_INIT(process_message_feefilter, initialize_process_message) { fuzz_target(buffer, "feefilter"); } +FUZZ_TARGET_INIT(process_message_filteradd, initialize_process_message) { fuzz_target(buffer, "filteradd"); } +FUZZ_TARGET_INIT(process_message_filterclear, initialize_process_message) { fuzz_target(buffer, "filterclear"); } +FUZZ_TARGET_INIT(process_message_filterload, initialize_process_message) { fuzz_target(buffer, "filterload"); } +FUZZ_TARGET_INIT(process_message_getaddr, initialize_process_message) { fuzz_target(buffer, "getaddr"); } +FUZZ_TARGET_INIT(process_message_getblocks, initialize_process_message) { fuzz_target(buffer, "getblocks"); } +FUZZ_TARGET_INIT(process_message_getblocktxn, initialize_process_message) { fuzz_target(buffer, "getblocktxn"); } +FUZZ_TARGET_INIT(process_message_getdata, initialize_process_message) { fuzz_target(buffer, "getdata"); } +FUZZ_TARGET_INIT(process_message_getheaders, initialize_process_message) { fuzz_target(buffer, "getheaders"); } +FUZZ_TARGET_INIT(process_message_headers, initialize_process_message) { fuzz_target(buffer, "headers"); } +FUZZ_TARGET_INIT(process_message_inv, initialize_process_message) { fuzz_target(buffer, "inv"); } +FUZZ_TARGET_INIT(process_message_mempool, initialize_process_message) { fuzz_target(buffer, "mempool"); } +FUZZ_TARGET_INIT(process_message_notfound, initialize_process_message) { fuzz_target(buffer, "notfound"); } +FUZZ_TARGET_INIT(process_message_ping, initialize_process_message) { fuzz_target(buffer, "ping"); } +FUZZ_TARGET_INIT(process_message_pong, initialize_process_message) { fuzz_target(buffer, "pong"); } +FUZZ_TARGET_INIT(process_message_sendcmpct, initialize_process_message) { fuzz_target(buffer, "sendcmpct"); } +FUZZ_TARGET_INIT(process_message_sendheaders, initialize_process_message) { fuzz_target(buffer, "sendheaders"); } +FUZZ_TARGET_INIT(process_message_tx, initialize_process_message) { fuzz_target(buffer, "tx"); } +FUZZ_TARGET_INIT(process_message_verack, initialize_process_message) { fuzz_target(buffer, "verack"); } +FUZZ_TARGET_INIT(process_message_version, initialize_process_message) { fuzz_target(buffer, "version"); } diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 19ea92b750..810f0aac92 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -17,55 +17,50 @@ #include <validation.h> #include <validationinterface.h> +namespace { const TestingSetup* g_setup; +} // namespace -void initialize() +void initialize_process_messages() { - static TestingSetup setup{ - CBaseChainParams::REGTEST, - { - "-nodebuglogfile", - }, - }; - g_setup = &setup; - + static const auto testing_setup = MakeFuzzingContext<const TestingSetup>(); + g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { MineBlock(g_setup->m_node, CScript() << OP_TRUE); } SyncWithValidationInterfaceQueue(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(process_messages, initialize_process_messages) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get(); TestChainState& chainstate = *(TestChainState*)&g_setup->m_node.chainman->ActiveChainstate(); + SetMockTime(1610000000); // any time to successfully reset ibd chainstate.ResetIbd(); - std::vector<CNode*> peers; - bool jump_out_of_ibd{false}; + std::vector<CNode*> peers; 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 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()); + peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release()); CNode& p2p_node = *peers.back(); - p2p_node.fSuccessfullyConnected = true; + const bool successfully_connected{true}; + p2p_node.fSuccessfullyConnected = successfully_connected; p2p_node.fPauseSend = false; - p2p_node.nVersion = PROTOCOL_VERSION; - p2p_node.SetCommonVersion(PROTOCOL_VERSION); g_setup->m_node.peerman->InitializeNode(&p2p_node); + FillNode(fuzzed_data_provider, p2p_node, /* init_version */ successfully_connected); connman.AddTestNode(p2p_node); } while (fuzzed_data_provider.ConsumeBool()) { - if (!jump_out_of_ibd) jump_out_of_ibd = fuzzed_data_provider.ConsumeBool(); - if (jump_out_of_ibd && chainstate.IsInitialBlockDownload()) chainstate.JumpOutOfIbd(); const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()}; + const auto mock_time = ConsumeTime(fuzzed_data_provider); + SetMockTime(mock_time); + CSerializedNetMsg net_msg; net_msg.m_type = random_message_type; net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); @@ -79,6 +74,10 @@ void test_one_input(const std::vector<uint8_t>& buffer) connman.ProcessMessagesOnce(random_node); } catch (const std::ios_base::failure&) { } + { + LOCK(random_node.cs_sendProcessing); + g_setup->m_node.peerman->SendMessages(&random_node); + } } SyncWithValidationInterfaceQueue(); LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement diff --git a/src/test/fuzz/protocol.cpp b/src/test/fuzz/protocol.cpp index 78df0f89e7..572181366b 100644 --- a/src/test/fuzz/protocol.cpp +++ b/src/test/fuzz/protocol.cpp @@ -12,7 +12,7 @@ #include <stdexcept> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(protocol) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::optional<CInv> inv = ConsumeDeserializable<CInv>(fuzzed_data_provider); diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp index 908e2b16f2..0b4588c4ce 100644 --- a/src/test/fuzz/psbt.cpp +++ b/src/test/fuzz/psbt.cpp @@ -17,12 +17,12 @@ #include <string> #include <vector> -void initialize() +void initialize_psbt() { static const ECCVerifyHandle verify_handle; } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(psbt, initialize_psbt) { PartiallySignedTransaction psbt_mut; const std::string raw_psbt{buffer.begin(), buffer.end()}; diff --git a/src/test/fuzz/random.cpp b/src/test/fuzz/random.cpp index 7df6594ad6..96668734fd 100644 --- a/src/test/fuzz/random.cpp +++ b/src/test/fuzz/random.cpp @@ -12,7 +12,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(random) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp index 1fd88a5f7b..26c89a70c3 100644 --- a/src/test/fuzz/rbf.cpp +++ b/src/test/fuzz/rbf.cpp @@ -15,9 +15,10 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(rbf) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + SetMockTime(ConsumeTime(fuzzed_data_provider)); std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); if (!mtx) { return; diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp index 623b8cff3a..2a08b45aa3 100644 --- a/src/test/fuzz/rolling_bloom_filter.cpp +++ b/src/test/fuzz/rolling_bloom_filter.cpp @@ -14,7 +14,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(rolling_bloom_filter) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); @@ -22,29 +22,27 @@ void test_one_input(const std::vector<uint8_t>& buffer) fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 1000), 0.999 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max())}; while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) { - case 0: { - const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); - (void)rolling_bloom_filter.contains(b); - rolling_bloom_filter.insert(b); - const bool present = rolling_bloom_filter.contains(b); - assert(present); - break; - } - case 1: { - const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider); - if (!u256) { - break; - } - (void)rolling_bloom_filter.contains(*u256); - rolling_bloom_filter.insert(*u256); - const bool present = rolling_bloom_filter.contains(*u256); - assert(present); - break; - } - case 2: - rolling_bloom_filter.reset(); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + (void)rolling_bloom_filter.contains(b); + rolling_bloom_filter.insert(b); + const bool present = rolling_bloom_filter.contains(b); + assert(present); + }, + [&] { + const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider); + if (!u256) { + return; + } + (void)rolling_bloom_filter.contains(*u256); + rolling_bloom_filter.insert(*u256); + const bool present = rolling_bloom_filter.contains(*u256); + assert(present); + }, + [&] { + rolling_bloom_filter.reset(); + }); } } diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp index 4274fa4351..7fadf36f98 100644 --- a/src/test/fuzz/script.cpp +++ b/src/test/fuzz/script.cpp @@ -29,7 +29,7 @@ #include <string> #include <vector> -void initialize() +void initialize_script() { // Fuzzers using pubkey must hold an ECCVerifyHandle. static const ECCVerifyHandle verify_handle; @@ -37,7 +37,7 @@ void initialize() SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(script, initialize_script) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::optional<CScript> script_opt = ConsumeDeserializable<CScript>(fuzzed_data_provider); @@ -71,7 +71,22 @@ void test_one_input(const std::vector<uint8_t>& buffer) (void)IsSolvable(signing_provider, script); TxoutType which_type; - (void)IsStandard(script, which_type); + bool is_standard_ret = IsStandard(script, which_type); + if (!is_standard_ret) { + assert(which_type == TxoutType::NONSTANDARD || + which_type == TxoutType::NULL_DATA || + which_type == TxoutType::MULTISIG); + } + if (which_type == TxoutType::NONSTANDARD) { + assert(!is_standard_ret); + } + if (which_type == TxoutType::NULL_DATA) { + assert(script.IsUnspendable()); + } + if (script.IsUnspendable()) { + assert(which_type == TxoutType::NULL_DATA || + which_type == TxoutType::NONSTANDARD); + } (void)RecursiveDynamicUsage(script); @@ -82,7 +97,6 @@ void test_one_input(const std::vector<uint8_t>& buffer) (void)script.IsPayToScriptHash(); (void)script.IsPayToWitnessScriptHash(); (void)script.IsPushOnly(); - (void)script.IsUnspendable(); (void)script.GetSigOpCount(/* fAccurate= */ false); (void)FormatScript(script); @@ -140,13 +154,13 @@ void test_one_input(const std::vector<uint8_t>& buffer) { WitnessUnknown witness_unknown_1{}; - witness_unknown_1.version = fuzzed_data_provider.ConsumeIntegral<int>(); + witness_unknown_1.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40); witness_unknown_1.length = witness_unknown_program_1.size(); std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown_1.program); WitnessUnknown witness_unknown_2{}; - witness_unknown_2.version = fuzzed_data_provider.ConsumeIntegral<int>(); + witness_unknown_2.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); const std::vector<uint8_t> witness_unknown_program_2 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40); witness_unknown_2.length = witness_unknown_program_2.size(); std::copy(witness_unknown_program_2.begin(), witness_unknown_program_2.end(), witness_unknown_2.program); diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp index d20fa43d68..2091ad5d91 100644 --- a/src/test/fuzz/script_assets_test_minimizer.cpp +++ b/src/test/fuzz/script_assets_test_minimizer.cpp @@ -188,9 +188,9 @@ void Test(const std::string& str) ECCVerifyHandle handle; -} +} // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(script_assets_test_minimizer) { if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return; const std::string str((const char*)buffer.data(), buffer.size() - 2); diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp index 22f4b4f44a..fcd66b234e 100644 --- a/src/test/fuzz/script_bitcoin_consensus.cpp +++ b/src/test/fuzz/script_bitcoin_consensus.cpp @@ -12,7 +12,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(script_bitcoin_consensus) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::vector<uint8_t> random_bytes_1 = ConsumeRandomLengthByteVector(fuzzed_data_provider); diff --git a/src/test/fuzz/script_descriptor_cache.cpp b/src/test/fuzz/script_descriptor_cache.cpp index 4bfe61cec7..1c62c018e7 100644 --- a/src/test/fuzz/script_descriptor_cache.cpp +++ b/src/test/fuzz/script_descriptor_cache.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(script_descriptor_cache) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); DescriptorCache descriptor_cache; diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp index 300c78fca0..ce8915ca2c 100644 --- a/src/test/fuzz/script_flags.cpp +++ b/src/test/fuzz/script_flags.cpp @@ -13,12 +13,12 @@ /** Flags that are not forbidden by an assert */ static bool IsValidFlagCombination(unsigned flags); -void initialize() +void initialize_script_flags() { static const ECCVerifyHandle verify_handle; } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(script_flags, initialize_script_flags) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { diff --git a/src/test/fuzz/script_interpreter.cpp b/src/test/fuzz/script_interpreter.cpp index 26d5732f24..5d59771682 100644 --- a/src/test/fuzz/script_interpreter.cpp +++ b/src/test/fuzz/script_interpreter.cpp @@ -15,7 +15,7 @@ bool CastToBool(const std::vector<unsigned char>& vch); -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(script_interpreter) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); { diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp index 7d24af20ac..bdbfe817ff 100644 --- a/src/test/fuzz/script_ops.cpp +++ b/src/test/fuzz/script_ops.cpp @@ -11,61 +11,58 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(script_ops) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CScript script = ConsumeScript(fuzzed_data_provider); while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) { - case 0: { - CScript s = ConsumeScript(fuzzed_data_provider); - script = std::move(s); - break; - } - case 1: { - const CScript& s = ConsumeScript(fuzzed_data_provider); - script = s; - break; - } - case 2: - script << fuzzed_data_provider.ConsumeIntegral<int64_t>(); - break; - case 3: - script << ConsumeOpcodeType(fuzzed_data_provider); - break; - case 4: - script << ConsumeScriptNum(fuzzed_data_provider); - break; - case 5: - script << ConsumeRandomLengthByteVector(fuzzed_data_provider); - break; - case 6: - script.clear(); - break; - case 7: { - (void)script.GetSigOpCount(false); - (void)script.GetSigOpCount(true); - (void)script.GetSigOpCount(script); - (void)script.HasValidOps(); - (void)script.IsPayToScriptHash(); - (void)script.IsPayToWitnessScriptHash(); - (void)script.IsPushOnly(); - (void)script.IsUnspendable(); - { - CScript::const_iterator pc = script.begin(); - opcodetype opcode; - (void)script.GetOp(pc, opcode); - std::vector<uint8_t> data; - (void)script.GetOp(pc, opcode, data); - (void)script.IsPushOnly(pc); - } - { - int version; - std::vector<uint8_t> program; - (void)script.IsWitnessProgram(version, program); - } - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + CScript s = ConsumeScript(fuzzed_data_provider); + script = std::move(s); + }, + [&] { + const CScript& s = ConsumeScript(fuzzed_data_provider); + script = s; + }, + [&] { + script << fuzzed_data_provider.ConsumeIntegral<int64_t>(); + }, + [&] { + script << ConsumeOpcodeType(fuzzed_data_provider); + }, + [&] { + script << ConsumeScriptNum(fuzzed_data_provider); + }, + [&] { + script << ConsumeRandomLengthByteVector(fuzzed_data_provider); + }, + [&] { + script.clear(); + }, + [&] { + (void)script.GetSigOpCount(false); + (void)script.GetSigOpCount(true); + (void)script.GetSigOpCount(script); + (void)script.HasValidOps(); + (void)script.IsPayToScriptHash(); + (void)script.IsPayToWitnessScriptHash(); + (void)script.IsPushOnly(); + (void)script.IsUnspendable(); + { + CScript::const_iterator pc = script.begin(); + opcodetype opcode; + (void)script.GetOp(pc, opcode); + std::vector<uint8_t> data; + (void)script.GetOp(pc, opcode, data); + (void)script.IsPushOnly(pc); + } + { + int version; + std::vector<uint8_t> program; + (void)script.IsWitnessProgram(version, program); + } + }); } } diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp index 87af71897b..f7e45d6889 100644 --- a/src/test/fuzz/script_sigcache.cpp +++ b/src/test/fuzz/script_sigcache.cpp @@ -16,7 +16,7 @@ #include <string> #include <vector> -void initialize() +void initialize_script_sigcache() { static const ECCVerifyHandle ecc_verify_handle; ECC_Start(); @@ -24,12 +24,12 @@ void initialize() InitSignatureCache(); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(script_sigcache, initialize_script_sigcache) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::optional<CMutableTransaction> mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); - const CTransaction tx = mutable_transaction ? CTransaction{*mutable_transaction} : CTransaction{}; + const CTransaction tx{mutable_transaction ? *mutable_transaction : CMutableTransaction{}}; const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); const CAmount amount = ConsumeMoney(fuzzed_data_provider); const bool store = fuzzed_data_provider.ConsumeBool(); diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp index c626f950e7..fe850a6959 100644 --- a/src/test/fuzz/script_sign.cpp +++ b/src/test/fuzz/script_sign.cpp @@ -22,14 +22,14 @@ #include <string> #include <vector> -void initialize() +void initialize_script_sign() { static const ECCVerifyHandle ecc_verify_handle; ECC_Start(); SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(script_sign, initialize_script_sign) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::vector<uint8_t> key = ConsumeRandomLengthByteVector(fuzzed_data_provider, 128); diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp index 68c1ae58ca..bc4867839c 100644 --- a/src/test/fuzz/scriptnum_ops.cpp +++ b/src/test/fuzz/scriptnum_ops.cpp @@ -24,110 +24,104 @@ bool IsValidSubtraction(const CScriptNum& lhs, const CScriptNum& rhs) } } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(scriptnum_ops) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CScriptNum script_num = ConsumeScriptNum(fuzzed_data_provider); while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) { - case 0: { - const int64_t i = fuzzed_data_provider.ConsumeIntegral<int64_t>(); - assert((script_num == i) != (script_num != i)); - assert((script_num <= i) != (script_num > i)); - assert((script_num >= i) != (script_num < i)); - // Avoid signed integer overflow: - // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long' - if (IsValidAddition(script_num, CScriptNum{i})) { - assert((script_num + i) - i == script_num); - } - // Avoid signed integer overflow: - // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long' - if (IsValidSubtraction(script_num, CScriptNum{i})) { - assert((script_num - i) + i == script_num); - } - break; - } - case 1: { - const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); - assert((script_num == random_script_num) != (script_num != random_script_num)); - assert((script_num <= random_script_num) != (script_num > random_script_num)); - assert((script_num >= random_script_num) != (script_num < random_script_num)); - // Avoid signed integer overflow: - // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long' - if (IsValidAddition(script_num, random_script_num)) { - assert((script_num + random_script_num) - random_script_num == script_num); - } - // Avoid signed integer overflow: - // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long' - if (IsValidSubtraction(script_num, random_script_num)) { - assert((script_num - random_script_num) + random_script_num == script_num); - } - break; - } - case 2: { - const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); - if (!IsValidAddition(script_num, random_script_num)) { - // Avoid assertion failure: - // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed. - break; - } - script_num += random_script_num; - break; - } - case 3: { - const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); - if (!IsValidSubtraction(script_num, random_script_num)) { - // Avoid assertion failure: - // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed. - break; - } - script_num -= random_script_num; - break; - } - case 4: - script_num = script_num & fuzzed_data_provider.ConsumeIntegral<int64_t>(); - break; - case 5: - script_num = script_num & ConsumeScriptNum(fuzzed_data_provider); - break; - case 6: - script_num &= ConsumeScriptNum(fuzzed_data_provider); - break; - case 7: - if (script_num == CScriptNum{std::numeric_limits<int64_t>::min()}) { - // Avoid assertion failure: - // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits<int64_t>::min()' failed. - break; - } - script_num = -script_num; - break; - case 8: - script_num = fuzzed_data_provider.ConsumeIntegral<int64_t>(); - break; - case 9: { - const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>(); - if (!IsValidAddition(script_num, CScriptNum{random_integer})) { - // Avoid assertion failure: - // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed. - break; - } - script_num += random_integer; - break; - } - case 10: { - const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>(); - if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) { - // Avoid assertion failure: - // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed. - break; - } - script_num -= random_integer; - break; - } - case 11: - script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>(); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + const int64_t i = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + assert((script_num == i) != (script_num != i)); + assert((script_num <= i) != (script_num > i)); + assert((script_num >= i) != (script_num < i)); + // Avoid signed integer overflow: + // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long' + if (IsValidAddition(script_num, CScriptNum{i})) { + assert((script_num + i) - i == script_num); + } + // Avoid signed integer overflow: + // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long' + if (IsValidSubtraction(script_num, CScriptNum{i})) { + assert((script_num - i) + i == script_num); + } + }, + [&] { + const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); + assert((script_num == random_script_num) != (script_num != random_script_num)); + assert((script_num <= random_script_num) != (script_num > random_script_num)); + assert((script_num >= random_script_num) != (script_num < random_script_num)); + // Avoid signed integer overflow: + // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long' + if (IsValidAddition(script_num, random_script_num)) { + assert((script_num + random_script_num) - random_script_num == script_num); + } + // Avoid signed integer overflow: + // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long' + if (IsValidSubtraction(script_num, random_script_num)) { + assert((script_num - random_script_num) + random_script_num == script_num); + } + }, + [&] { + const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); + if (!IsValidAddition(script_num, random_script_num)) { + // Avoid assertion failure: + // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed. + return; + } + script_num += random_script_num; + }, + [&] { + const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); + if (!IsValidSubtraction(script_num, random_script_num)) { + // Avoid assertion failure: + // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed. + return; + } + script_num -= random_script_num; + }, + [&] { + script_num = script_num & fuzzed_data_provider.ConsumeIntegral<int64_t>(); + }, + [&] { + script_num = script_num & ConsumeScriptNum(fuzzed_data_provider); + }, + [&] { + script_num &= ConsumeScriptNum(fuzzed_data_provider); + }, + [&] { + if (script_num == CScriptNum{std::numeric_limits<int64_t>::min()}) { + // Avoid assertion failure: + // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits<int64_t>::min()' failed. + return; + } + script_num = -script_num; + }, + [&] { + script_num = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + }, + [&] { + const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + if (!IsValidAddition(script_num, CScriptNum{random_integer})) { + // Avoid assertion failure: + // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed. + return; + } + script_num += random_integer; + }, + [&] { + const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) { + // Avoid assertion failure: + // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed. + return; + } + script_num -= random_integer; + }, + [&] { + script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>(); + }); (void)script_num.getint(); (void)script_num.getvch(); } diff --git a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp index d4f302a8d3..0435626356 100644 --- a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp +++ b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp @@ -14,7 +14,7 @@ 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) +FUZZ_TARGET(secp256k1_ec_seckey_import_export_der) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; secp256k1_context* secp256k1_context_sign = secp256k1_context_create(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 index ed8c7aba89..f437d53b57 100644 --- a/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp +++ b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp @@ -14,7 +14,7 @@ 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) +FUZZ_TARGET(secp256k1_ecdsa_signature_parse_der_lax) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const std::vector<uint8_t> signature_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider); diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp index e121c89665..3e7b72805e 100644 --- a/src/test/fuzz/signature_checker.cpp +++ b/src/test/fuzz/signature_checker.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void initialize() +void initialize_signature_checker() { static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); } @@ -24,7 +24,7 @@ class FuzzedSignatureChecker : public BaseSignatureChecker FuzzedDataProvider& m_fuzzed_data_provider; public: - FuzzedSignatureChecker(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider(fuzzed_data_provider) + explicit FuzzedSignatureChecker(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider(fuzzed_data_provider) { } @@ -52,7 +52,7 @@ public: }; } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(signature_checker, initialize_signature_checker) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp index 786f1a83fe..83effec064 100644 --- a/src/test/fuzz/signet.cpp +++ b/src/test/fuzz/signet.cpp @@ -15,12 +15,12 @@ #include <optional> #include <vector> -void initialize() +void initialize_signet() { - InitializeFuzzingContext(CBaseChainParams::SIGNET); + static const auto testing_setup = MakeFuzzingContext<>(CBaseChainParams::SIGNET); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(signet, initialize_signet) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const std::optional<CBlock> block = ConsumeDeserializable<CBlock>(fuzzed_data_provider); diff --git a/src/test/fuzz/span.cpp b/src/test/fuzz/span.cpp index f6b6e8f6f0..8f753948df 100644 --- a/src/test/fuzz/span.cpp +++ b/src/test/fuzz/span.cpp @@ -13,7 +13,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(span) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/spanparsing.cpp b/src/test/fuzz/spanparsing.cpp index e5bf5dd608..b8996632bc 100644 --- a/src/test/fuzz/spanparsing.cpp +++ b/src/test/fuzz/spanparsing.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -6,7 +6,7 @@ #include <test/fuzz/fuzz.h> #include <util/spanparsing.h> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(spanparsing) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const size_t query_size = fuzzed_data_provider.ConsumeIntegral<size_t>(); diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp index 271062dc95..282a2cd8ca 100644 --- a/src/test/fuzz/string.cpp +++ b/src/test/fuzz/string.cpp @@ -33,7 +33,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(string) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string random_string_1 = fuzzed_data_provider.ConsumeRandomLengthString(32); diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp index 29064bc45c..b66a7abfb3 100644 --- a/src/test/fuzz/strprintf.cpp +++ b/src/test/fuzz/strprintf.cpp @@ -4,6 +4,7 @@ #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> #include <tinyformat.h> #include <util/strencodings.h> #include <util/translation.h> @@ -13,7 +14,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(str_printf) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string format_string = fuzzed_data_provider.ConsumeRandomLengthString(64); @@ -109,32 +110,32 @@ void test_one_input(const std::vector<uint8_t>& buffer) } try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) { - case 0: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); - break; - case 1: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); - break; - case 2: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>()); - break; - case 3: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>()); - break; - case 4: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>()); - break; - case 5: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool()); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool()); + }); } catch (const tinyformat::format_error&) { } @@ -155,40 +156,40 @@ void test_one_input(const std::vector<uint8_t>& buffer) } try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) { - case 0: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>()); - break; - case 1: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>()); - break; - case 2: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>()); - break; - case 3: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>()); - break; - case 4: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>()); - break; - case 5: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); - break; - case 6: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>()); - break; - case 7: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + }); } catch (const tinyformat::format_error&) { } } diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 01b523cee4..47b38b6d23 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -22,7 +22,7 @@ std::string GetArgumentName(const std::string& name) } } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(system) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); ArgsManager args_manager{}; @@ -32,71 +32,63 @@ void test_one_input(const std::vector<uint8_t>& buffer) } while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 7)) { - case 0: { - args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16)); - break; - } - case 1: { - args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); - break; - } - case 2: { - args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); - break; - } - case 3: { - args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool()); - break; - } - case 4: { - const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN}); - // Avoid hitting: - // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. - const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16)); - if (args_manager.GetArgFlags(argument_name) != nullopt) { - break; - } - args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>(), options_category); - break; - } - case 5: { - // Avoid hitting: - // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. - const std::vector<std::string> names = ConsumeRandomLengthStringVector(fuzzed_data_provider); - std::vector<std::string> hidden_arguments; - for (const std::string& name : names) { - const std::string hidden_argument = GetArgumentName(name); - if (args_manager.GetArgFlags(hidden_argument) != nullopt) { - continue; + CallOneOf( + fuzzed_data_provider, + [&] { + args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16)); + }, + [&] { + args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); + }, + [&] { + args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); + }, + [&] { + args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool()); + }, + [&] { + const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN}); + // Avoid hitting: + // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. + const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16)); + if (args_manager.GetArgFlags(argument_name) != nullopt) { + return; } - if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) { - continue; + args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>(), options_category); + }, + [&] { + // Avoid hitting: + // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. + const std::vector<std::string> names = ConsumeRandomLengthStringVector(fuzzed_data_provider); + std::vector<std::string> hidden_arguments; + for (const std::string& name : names) { + const std::string hidden_argument = GetArgumentName(name); + if (args_manager.GetArgFlags(hidden_argument) != nullopt) { + continue; + } + if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) { + continue; + } + hidden_arguments.push_back(hidden_argument); } - hidden_arguments.push_back(hidden_argument); - } - args_manager.AddHiddenArgs(hidden_arguments); - break; - } - case 6: { - args_manager.ClearArgs(); - break; - } - case 7: { - const std::vector<std::string> random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider); - std::vector<const char*> argv; - argv.reserve(random_arguments.size()); - for (const std::string& random_argument : random_arguments) { - argv.push_back(random_argument.c_str()); - } - try { - std::string error; - (void)args_manager.ParseParameters(argv.size(), argv.data(), error); - } catch (const std::logic_error&) { - } - break; - } - } + args_manager.AddHiddenArgs(hidden_arguments); + }, + [&] { + args_manager.ClearArgs(); + }, + [&] { + const std::vector<std::string> random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider); + std::vector<const char*> argv; + argv.reserve(random_arguments.size()); + for (const std::string& random_argument : random_arguments) { + argv.push_back(random_argument.c_str()); + } + try { + std::string error; + (void)args_manager.ParseParameters(argv.size(), argv.data(), error); + } catch (const std::logic_error&) { + } + }); } const std::string s1 = fuzzed_data_provider.ConsumeRandomLengthString(16); diff --git a/src/test/fuzz/timedata.cpp b/src/test/fuzz/timedata.cpp index a0e579a88f..d7fa66298a 100644 --- a/src/test/fuzz/timedata.cpp +++ b/src/test/fuzz/timedata.cpp @@ -11,7 +11,7 @@ #include <string> #include <vector> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(timedata) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const unsigned int max_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000); diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index 4f972dea1c..13ae450756 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -21,12 +21,12 @@ #include <cassert> -void initialize() +void initialize_transaction() { SelectParams(CBaseChainParams::REGTEST); } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(transaction, initialize_transaction) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { @@ -42,7 +42,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) return CTransaction(deserialize, ds); } catch (const std::ios_base::failure&) { valid_tx = false; - return CTransaction(); + return CTransaction{CMutableTransaction{}}; } }(); bool valid_mutable_tx = true; diff --git a/src/test/fuzz/tx_in.cpp b/src/test/fuzz/tx_in.cpp index 8e116537d1..f8247c1fa4 100644 --- a/src/test/fuzz/tx_in.cpp +++ b/src/test/fuzz/tx_in.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -12,7 +12,7 @@ #include <cassert> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(tx_in) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); CTxIn tx_in; diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp index aa1338d5ba..39a50b6c80 100644 --- a/src/test/fuzz/tx_out.cpp +++ b/src/test/fuzz/tx_out.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -10,7 +10,7 @@ #include <test/fuzz/fuzz.h> #include <version.h> -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(tx_out) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); CTxOut tx_out; diff --git a/src/test/fuzz/txrequest.cpp b/src/test/fuzz/txrequest.cpp index 9529ad3274..72438ff2d7 100644 --- a/src/test/fuzz/txrequest.cpp +++ b/src/test/fuzz/txrequest.cpp @@ -310,7 +310,7 @@ public: }; } // namespace -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET(txrequest) { // Tester object (which encapsulates a TxRequestTracker). Tester tester; diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp new file mode 100644 index 0000000000..0a541e4186 --- /dev/null +++ b/src/test/fuzz/util.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/fuzz/util.h> +#include <version.h> + +void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept +{ + const ServiceFlags remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); + const NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS); + const int32_t version = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max()); + const bool filter_txs = fuzzed_data_provider.ConsumeBool(); + + node.nServices = remote_services; + node.m_permissionFlags = permission_flags; + if (init_version) { + node.nVersion = version; + node.SetCommonVersion(std::min(version, PROTOCOL_VERSION)); + } + if (node.m_tx_relay != nullptr) { + LOCK(node.m_tx_relay->cs_filter); + node.m_tx_relay->fRelayTxes = filter_txs; + } +} diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 481ffa3da4..7a2dcfe84a 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -22,10 +22,12 @@ #include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> +#include <test/util/net.h> #include <test/util/setup_common.h> #include <txmempool.h> #include <uint256.h> #include <util/time.h> +#include <util/vector.h> #include <version.h> #include <algorithm> @@ -35,23 +37,34 @@ #include <string> #include <vector> -NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept +template <typename... Callables> +void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables) +{ + constexpr size_t call_size{sizeof...(callables)}; + static_assert(call_size >= 1); + const size_t call_index{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)}; + + size_t i{0}; + return ((i++ == call_index ? callables() : void()), ...); +} + +[[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length); return {s.begin(), s.end()}; } -NODISCARD inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept +[[nodiscard]] inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length)); } -NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept +[[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { return CDataStream{ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION}; } -NODISCARD inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept +[[nodiscard]] inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept { const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); std::vector<std::string> r; @@ -62,7 +75,7 @@ NODISCARD inline std::vector<std::string> ConsumeRandomLengthStringVector(Fuzzed } template <typename T> -NODISCARD inline std::vector<T> ConsumeRandomLengthIntegralVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16) noexcept +[[nodiscard]] inline std::vector<T> ConsumeRandomLengthIntegralVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16) noexcept { const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); std::vector<T> r; @@ -73,7 +86,7 @@ NODISCARD inline std::vector<T> ConsumeRandomLengthIntegralVector(FuzzedDataProv } template <typename T> -NODISCARD inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept +[[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length); CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION}; @@ -86,35 +99,44 @@ NODISCARD inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzz return obj; } -NODISCARD inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept +template <typename WeakEnumType, size_t size> +[[nodiscard]] WeakEnumType ConsumeWeakEnum(FuzzedDataProvider& fuzzed_data_provider, const WeakEnumType (&all_types)[size]) noexcept +{ + return fuzzed_data_provider.ConsumeBool() ? + fuzzed_data_provider.PickValueInArray<WeakEnumType>(all_types) : + WeakEnumType(fuzzed_data_provider.ConsumeIntegral<typename std::underlying_type<WeakEnumType>::type>()); +} + +[[nodiscard]] inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept { return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE)); } -NODISCARD inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider) noexcept { return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY); } -NODISCARD inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept { - static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:00Z"); + // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) is a no-op. + static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z"); static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z"); return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max); } -NODISCARD inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept { const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); return {b.begin(), b.end()}; } -NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_provider) noexcept { return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()}; } -NODISCARD inline uint160 ConsumeUInt160(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline uint160 ConsumeUInt160(FuzzedDataProvider& fuzzed_data_provider) noexcept { const std::vector<uint8_t> v160 = fuzzed_data_provider.ConsumeBytes<uint8_t>(160 / 8); if (v160.size() != 160 / 8) { @@ -123,7 +145,7 @@ NODISCARD inline uint160 ConsumeUInt160(FuzzedDataProvider& fuzzed_data_provider return uint160{v160}; } -NODISCARD inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept { const std::vector<uint8_t> v256 = fuzzed_data_provider.ConsumeBytes<uint8_t>(256 / 8); if (v256.size() != 256 / 8) { @@ -132,12 +154,12 @@ NODISCARD inline uint256 ConsumeUInt256(FuzzedDataProvider& fuzzed_data_provider return uint256{v256}; } -NODISCARD inline arith_uint256 ConsumeArithUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline arith_uint256 ConsumeArithUInt256(FuzzedDataProvider& fuzzed_data_provider) noexcept { return UintToArith256(ConsumeUInt256(fuzzed_data_provider)); } -NODISCARD inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept +[[nodiscard]] inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept { // Avoid: // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long' @@ -152,45 +174,39 @@ NODISCARD inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzze return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}}; } -NODISCARD inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept { CTxDestination tx_destination; - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) { - case 0: { - tx_destination = CNoDestination{}; - break; - } - case 1: { - tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; - break; - } - case 2: { - tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)}; - break; - } - case 3: { - tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)}; - break; - } - case 4: { - tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)}; - break; - } - case 5: { - WitnessUnknown witness_unknown{}; - witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<int>(); - const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40); - witness_unknown.length = witness_unknown_program_1.size(); - std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program); - tx_destination = witness_unknown; - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + tx_destination = CNoDestination{}; + }, + [&] { + tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; + }, + [&] { + tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)}; + }, + [&] { + tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)}; + }, + [&] { + tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)}; + }, + [&] { + WitnessUnknown witness_unknown{}; + witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40); + witness_unknown.length = witness_unknown_program_1.size(); + std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program); + tx_destination = witness_unknown; + }); return tx_destination; } template <typename T> -NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept +[[nodiscard]] bool MultiplicationOverflow(const T i, const T j) noexcept { static_assert(std::is_integral<T>::value, "Integral required."); if (std::numeric_limits<T>::is_signed) { @@ -213,7 +229,7 @@ NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept } template <class T> -NODISCARD bool AdditionOverflow(const T i, const T j) noexcept +[[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept { static_assert(std::is_integral<T>::value, "Integral required."); if (std::numeric_limits<T>::is_signed) { @@ -223,7 +239,7 @@ NODISCARD bool AdditionOverflow(const T i, const T j) noexcept return std::numeric_limits<T>::max() - i < j; } -NODISCARD inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept +[[nodiscard]] inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept { for (const CTxIn& tx_in : tx.vin) { const Coin& coin = inputs.AccessCoin(tx_in.prevout); @@ -238,7 +254,7 @@ NODISCARD inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsVie * Returns a byte vector of specified size regardless of the number of remaining bytes available * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size. */ -NODISCARD inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t length) noexcept +[[nodiscard]] inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t length) noexcept { std::vector<uint8_t> result(length); const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(length); @@ -248,13 +264,13 @@ NODISCARD inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataPro return result; } -CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept +inline CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept { const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION}); CNetAddr net_addr; if (network == Network::NET_IPV4) { - const in_addr v4_addr = { - .s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; + in_addr v4_addr = {}; + v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); net_addr = CNetAddr{v4_addr}; } else if (network == Network::NET_IPV6) { if (fuzzed_data_provider.remaining_bytes() >= 16) { @@ -270,40 +286,55 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept return net_addr; } -CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept +inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept { return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()}; } -CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept +inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept { return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()}; } -CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept +inline CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept { - return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()), fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; + return {ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; } -CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept +template <bool ReturnUniquePtr = false> +auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept { - const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>(); - const ServiceFlags local_services = static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - const int my_starting_height = fuzzed_data_provider.ConsumeIntegral<int>(); + const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegral<NodeId>()); + const ServiceFlags local_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); const SOCKET socket = INVALID_SOCKET; const CAddress address = ConsumeAddress(fuzzed_data_provider); const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider); const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64); - const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}); - const bool inbound_onion = fuzzed_data_provider.ConsumeBool(); - return {node_id, local_services, my_starting_height, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion}; + const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES); + const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false}; + if constexpr (ReturnUniquePtr) { + return std::make_unique<CNode>(node_id, local_services, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion); + } else { + return CNode{node_id, local_services, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion}; + } } +inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, node_id_in); } + +void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept; -void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST) +template <class T = const BasicTestingSetup> +std::unique_ptr<T> MakeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector<const char*>& extra_args = {}) { - static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}}; + // Prepend default arguments for fuzzing + const std::vector<const char*> arguments = Cat( + { + "-nodebuglogfile", + }, + extra_args); + + return MakeUnique<T>(chain_name, arguments); } class FuzzedFileProvider @@ -322,32 +353,26 @@ public: return nullptr; } std::string mode; - switch (m_fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) { - case 0: { - mode = "r"; - break; - } - case 1: { - mode = "r+"; - break; - } - case 2: { - mode = "w"; - break; - } - case 3: { - mode = "w+"; - break; - } - case 4: { - mode = "a"; - break; - } - case 5: { - mode = "a+"; - break; - } - } + CallOneOf( + m_fuzzed_data_provider, + [&] { + mode = "r"; + }, + [&] { + mode = "r+"; + }, + [&] { + mode = "w"; + }, + [&] { + mode = "w+"; + }, + [&] { + mode = "a"; + }, + [&] { + mode = "a+"; + }); #ifdef _GNU_SOURCE const cookie_io_functions_t io_hooks = { FuzzedFileProvider::read, @@ -419,7 +444,7 @@ public: } }; -NODISCARD inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept { return {fuzzed_data_provider}; } @@ -440,71 +465,69 @@ public: } }; -NODISCARD inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept { return {fuzzed_data_provider}; } -#define WRITE_TO_STREAM_CASE(id, type, consume) \ - case id: { \ - type o = consume; \ - stream << o; \ - break; \ +#define WRITE_TO_STREAM_CASE(type, consume) \ + [&] { \ + type o = consume; \ + stream << o; \ } template <typename Stream> void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept { while (fuzzed_data_provider.ConsumeBool()) { try { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) { - WRITE_TO_STREAM_CASE(0, bool, fuzzed_data_provider.ConsumeBool()) - WRITE_TO_STREAM_CASE(1, char, fuzzed_data_provider.ConsumeIntegral<char>()) - WRITE_TO_STREAM_CASE(2, int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()) - WRITE_TO_STREAM_CASE(3, uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()) - WRITE_TO_STREAM_CASE(4, int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()) - WRITE_TO_STREAM_CASE(5, uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>()) - WRITE_TO_STREAM_CASE(6, int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>()) - WRITE_TO_STREAM_CASE(7, uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()) - WRITE_TO_STREAM_CASE(8, int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()) - WRITE_TO_STREAM_CASE(9, uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()) - WRITE_TO_STREAM_CASE(10, float, fuzzed_data_provider.ConsumeFloatingPoint<float>()) - WRITE_TO_STREAM_CASE(11, double, fuzzed_data_provider.ConsumeFloatingPoint<double>()) - WRITE_TO_STREAM_CASE(12, std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)) - WRITE_TO_STREAM_CASE(13, std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider)) - } + CallOneOf( + fuzzed_data_provider, + WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()), + WRITE_TO_STREAM_CASE(char, fuzzed_data_provider.ConsumeIntegral<char>()), + WRITE_TO_STREAM_CASE(int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()), + WRITE_TO_STREAM_CASE(uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()), + WRITE_TO_STREAM_CASE(int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()), + WRITE_TO_STREAM_CASE(uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>()), + WRITE_TO_STREAM_CASE(int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>()), + WRITE_TO_STREAM_CASE(uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()), + WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()), + WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()), + WRITE_TO_STREAM_CASE(float, fuzzed_data_provider.ConsumeFloatingPoint<float>()), + WRITE_TO_STREAM_CASE(double, fuzzed_data_provider.ConsumeFloatingPoint<double>()), + WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)), + WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider))); } catch (const std::ios_base::failure&) { break; } } } -#define READ_FROM_STREAM_CASE(id, type) \ - case id: { \ - type o; \ - stream >> o; \ - break; \ +#define READ_FROM_STREAM_CASE(type) \ + [&] { \ + type o; \ + stream >> o; \ } template <typename Stream> void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept { while (fuzzed_data_provider.ConsumeBool()) { try { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) { - READ_FROM_STREAM_CASE(0, bool) - READ_FROM_STREAM_CASE(1, char) - READ_FROM_STREAM_CASE(2, int8_t) - READ_FROM_STREAM_CASE(3, uint8_t) - READ_FROM_STREAM_CASE(4, int16_t) - READ_FROM_STREAM_CASE(5, uint16_t) - READ_FROM_STREAM_CASE(6, int32_t) - READ_FROM_STREAM_CASE(7, uint32_t) - READ_FROM_STREAM_CASE(8, int64_t) - READ_FROM_STREAM_CASE(9, uint64_t) - READ_FROM_STREAM_CASE(10, float) - READ_FROM_STREAM_CASE(11, double) - READ_FROM_STREAM_CASE(12, std::string) - READ_FROM_STREAM_CASE(13, std::vector<char>) - } + CallOneOf( + fuzzed_data_provider, + READ_FROM_STREAM_CASE(bool), + READ_FROM_STREAM_CASE(char), + READ_FROM_STREAM_CASE(int8_t), + READ_FROM_STREAM_CASE(uint8_t), + READ_FROM_STREAM_CASE(int16_t), + READ_FROM_STREAM_CASE(uint16_t), + READ_FROM_STREAM_CASE(int32_t), + READ_FROM_STREAM_CASE(uint32_t), + READ_FROM_STREAM_CASE(int64_t), + READ_FROM_STREAM_CASE(uint64_t), + READ_FROM_STREAM_CASE(float), + READ_FROM_STREAM_CASE(double), + READ_FROM_STREAM_CASE(std::string), + READ_FROM_STREAM_CASE(std::vector<char>)); } catch (const std::ios_base::failure&) { break; } diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp index b0d4de89f3..73463b071e 100644 --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -17,8 +17,8 @@ BOOST_FIXTURE_TEST_SUITE(interfaces_tests, TestChain100Setup) BOOST_AUTO_TEST_CASE(findBlock) { - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); + auto& chain = m_node.chain; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); uint256 hash; BOOST_CHECK(chain->findBlock(active[10]->GetBlockHash(), FoundBlock().hash(hash))); @@ -44,13 +44,25 @@ BOOST_AUTO_TEST_CASE(findBlock) BOOST_CHECK(chain->findBlock(active[60]->GetBlockHash(), FoundBlock().mtpTime(mtp_time))); BOOST_CHECK_EQUAL(mtp_time, active[60]->GetMedianTimePast()); + bool cur_active{false}, next_active{false}; + uint256 next_hash; + BOOST_CHECK_EQUAL(active.Height(), 100); + BOOST_CHECK(chain->findBlock(active[99]->GetBlockHash(), FoundBlock().inActiveChain(cur_active).nextBlock(FoundBlock().inActiveChain(next_active).hash(next_hash)))); + BOOST_CHECK(cur_active); + BOOST_CHECK(next_active); + BOOST_CHECK_EQUAL(next_hash, active[100]->GetBlockHash()); + cur_active = next_active = false; + BOOST_CHECK(chain->findBlock(active[100]->GetBlockHash(), FoundBlock().inActiveChain(cur_active).nextBlock(FoundBlock().inActiveChain(next_active)))); + BOOST_CHECK(cur_active); + BOOST_CHECK(!next_active); + BOOST_CHECK(!chain->findBlock({}, FoundBlock())); } BOOST_AUTO_TEST_CASE(findFirstBlockWithTimeAndHeight) { - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); + auto& chain = m_node.chain; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); uint256 hash; int height; BOOST_CHECK(chain->findFirstBlockWithTimeAndHeight(/* min_time= */ 0, /* min_height= */ 5, FoundBlock().hash(hash).height(height))); @@ -59,25 +71,10 @@ BOOST_AUTO_TEST_CASE(findFirstBlockWithTimeAndHeight) BOOST_CHECK(!chain->findFirstBlockWithTimeAndHeight(/* min_time= */ active.Tip()->GetBlockTimeMax() + 1, /* min_height= */ 0)); } -BOOST_AUTO_TEST_CASE(findNextBlock) -{ - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); - bool reorg; - uint256 hash; - BOOST_CHECK(chain->findNextBlock(active[20]->GetBlockHash(), 20, FoundBlock().hash(hash), &reorg)); - BOOST_CHECK_EQUAL(hash, active[21]->GetBlockHash()); - BOOST_CHECK_EQUAL(reorg, false); - BOOST_CHECK(!chain->findNextBlock(uint256(), 20, {}, &reorg)); - BOOST_CHECK_EQUAL(reorg, true); - BOOST_CHECK(!chain->findNextBlock(active.Tip()->GetBlockHash(), active.Height(), {}, &reorg)); - BOOST_CHECK_EQUAL(reorg, false); -} - BOOST_AUTO_TEST_CASE(findAncestorByHeight) { - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); + auto& chain = m_node.chain; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); uint256 hash; BOOST_CHECK(chain->findAncestorByHeight(active[20]->GetBlockHash(), 10, FoundBlock().hash(hash))); BOOST_CHECK_EQUAL(hash, active[10]->GetBlockHash()); @@ -86,8 +83,8 @@ BOOST_AUTO_TEST_CASE(findAncestorByHeight) BOOST_AUTO_TEST_CASE(findAncestorByHash) { - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); + auto& chain = m_node.chain; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); int height = -1; BOOST_CHECK(chain->findAncestorByHash(active[20]->GetBlockHash(), active[10]->GetBlockHash(), FoundBlock().height(height))); BOOST_CHECK_EQUAL(height, 10); @@ -96,8 +93,8 @@ BOOST_AUTO_TEST_CASE(findAncestorByHash) BOOST_AUTO_TEST_CASE(findCommonAncestor) { - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); + auto& chain = m_node.chain; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); auto* orig_tip = active.Tip(); for (int i = 0; i < 10; ++i) { BlockValidationState state; @@ -126,8 +123,8 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor) BOOST_AUTO_TEST_CASE(hasBlocks) { - auto chain = interfaces::MakeChain(m_node); - auto& active = ChainActive(); + auto& chain = m_node.chain; + const CChain& active = Assert(m_node.chainman)->ActiveChain(); // Test ranges BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 10, 90)); diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 3362b8d17c..cb66d5164e 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -172,20 +172,30 @@ BOOST_AUTO_TEST_CASE(key_signature_tests) } BOOST_CHECK(found); - // When entropy is not specified, we should always see low R signatures that are less than 70 bytes in 256 tries + // When entropy is not specified, we should always see low R signatures that are less than or equal to 70 bytes in 256 tries + // The low R signatures should always have the value of their "length of R" byte less than or equal to 32 // We should see at least one signature that is less than 70 bytes. - found = true; bool found_small = false; + bool found_big = false; + bool bad_sign = false; for (int i = 0; i < 256; ++i) { sig.clear(); std::string msg = "A message to be signed" + ToString(i); msg_hash = Hash(msg); - BOOST_CHECK(key.Sign(msg_hash, sig)); - found = sig[3] == 0x20; - BOOST_CHECK(sig.size() <= 70); + if (!key.Sign(msg_hash, sig)) { + bad_sign = true; + break; + } + // sig.size() > 70 implies sig[3] > 32, because S is always low. + // But check both conditions anyway, just in case this implication is broken for some reason + if (sig[3] > 32 || sig.size() > 70) { + found_big = true; + break; + } found_small |= sig.size() < 70; } - BOOST_CHECK(found); + BOOST_CHECK(!bad_sign); + BOOST_CHECK(!found_big); BOOST_CHECK(found_small); } diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 9bc7cc5dab..942d54ede8 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 3de79a9f45..e967273636 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2019 The Bitcoin Core developers +// Copyright (c) 2011-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. diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index b23fec9b95..a1b41e17ed 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -9,6 +9,7 @@ #include <cstdint> #include <net.h> #include <netbase.h> +#include <optional.h> #include <serialize.h> #include <span.h> #include <streams.h> @@ -21,6 +22,7 @@ #include <boost/test/unit_test.hpp> +#include <algorithm> #include <ios> #include <memory> #include <string> @@ -75,7 +77,7 @@ public: } }; -static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman) +static CDataStream AddrmanToStream(const CAddrManSerializationMock& _addrman) { CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); ssPeersIn << Params().MessageStart(); @@ -179,7 +181,6 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) { SOCKET hSocket = INVALID_SOCKET; NodeId id = 0; - int height = 0; in_addr ipv4Addr; ipv4Addr.s_addr = 0xa0b0c001; @@ -188,7 +189,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) std::string pszDest; std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>( - id++, NODE_NETWORK, height, hSocket, addr, + id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 0, /* nLocalHostNonceIn = */ 0, CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY); @@ -198,10 +199,11 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode1->IsFeelerConn() == false); BOOST_CHECK(pnode1->IsAddrFetchConn() == false); BOOST_CHECK(pnode1->IsInboundConn() == false); + BOOST_CHECK(pnode1->IsInboundOnion() == false); BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4); std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>( - id++, NODE_NETWORK, height, hSocket, addr, + id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 1, /* nLocalHostNonceIn = */ 1, CAddress(), pszDest, ConnectionType::INBOUND, @@ -212,24 +214,26 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode2->IsFeelerConn() == false); BOOST_CHECK(pnode2->IsAddrFetchConn() == false); BOOST_CHECK(pnode2->IsInboundConn() == true); + BOOST_CHECK(pnode2->IsInboundOnion() == false); BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4); std::unique_ptr<CNode> pnode3 = MakeUnique<CNode>( - id++, NODE_NETWORK, height, hSocket, addr, + id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 0, /* nLocalHostNonceIn = */ 0, CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY, - /* inbound_onion = */ true); + /* inbound_onion = */ false); 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(pnode3->IsInboundOnion() == false); BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4); std::unique_ptr<CNode> pnode4 = MakeUnique<CNode>( - id++, NODE_NETWORK, height, hSocket, addr, + id++, NODE_NETWORK, hSocket, addr, /* nKeyedNetGroupIn = */ 1, /* nLocalHostNonceIn = */ 1, CAddress(), pszDest, ConnectionType::INBOUND, @@ -240,6 +244,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) BOOST_CHECK(pnode4->IsFeelerConn() == false); BOOST_CHECK(pnode4->IsAddrFetchConn() == false); BOOST_CHECK(pnode4->IsInboundConn() == true); + BOOST_CHECK(pnode4->IsInboundOnion() == true); BOOST_CHECK_EQUAL(pnode4->ConnectedThroughNetwork(), Network::NET_ONION); } @@ -604,6 +609,16 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_CHECK_EQUAL(addr.ToString(), "fc00:1:2:3:4:5:6:7"); BOOST_REQUIRE(s.empty()); + // Invalid CJDNS, wrong prefix. + s << MakeSpan(ParseHex("06" // network type (CJDNS) + "10" // address length + "aa000001000200030004000500060007" // address + )); + s >> addr; + BOOST_CHECK(addr.IsCJDNS()); + BOOST_CHECK(!addr.IsValid()); + BOOST_REQUIRE(s.empty()); + // Invalid CJDNS, with bogus length. s << MakeSpan(ParseHex("06" // network type (CJDNS) "01" // address length @@ -664,7 +679,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{}, ConnectionType::OUTBOUND_FULL_RELAY); + std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 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 @@ -771,4 +786,147 @@ BOOST_AUTO_TEST_CASE(PoissonNextSend) g_mock_deterministic_tests = false; } +std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context) +{ + std::vector<NodeEvictionCandidate> candidates; + for (int id = 0; id < n_candidates; ++id) { + candidates.push_back({ + /* id */ id, + /* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)), + /* nMinPingUsecTime */ static_cast<int64_t>(random_context.randrange(100)), + /* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)), + /* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)), + /* fRelevantServices */ random_context.randbool(), + /* fRelayTxes */ random_context.randbool(), + /* fBloomFilter */ random_context.randbool(), + /* nKeyedNetGroup */ random_context.randrange(100), + /* prefer_evict */ random_context.randbool(), + /* m_is_local */ random_context.randbool(), + }); + } + return candidates; +} + +// Returns true if any of the node ids in node_ids are selected for eviction. +bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::vector<NodeId>& node_ids, FastRandomContext& random_context) +{ + Shuffle(candidates.begin(), candidates.end(), random_context); + const Optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates)); + if (!evicted_node_id) { + return false; + } + return std::find(node_ids.begin(), node_ids.end(), *evicted_node_id) != node_ids.end(); +} + +// Create number_of_nodes random nodes, apply setup function candidate_setup_fn, +// apply eviction logic and then return true if any of the node ids in node_ids +// are selected for eviction. +bool IsEvicted(const int number_of_nodes, std::function<void(NodeEvictionCandidate&)> candidate_setup_fn, const std::vector<NodeId>& node_ids, FastRandomContext& random_context) +{ + std::vector<NodeEvictionCandidate> candidates = GetRandomNodeEvictionCandidates(number_of_nodes, random_context); + for (NodeEvictionCandidate& candidate : candidates) { + candidate_setup_fn(candidate); + } + return IsEvicted(candidates, node_ids, random_context); +} + +namespace { +constexpr int NODE_EVICTION_TEST_ROUNDS{10}; +constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200}; +} // namespace + +BOOST_AUTO_TEST_CASE(node_eviction_test) +{ + FastRandomContext random_context{true}; + + for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) { + for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) { + // Four nodes with the highest keyed netgroup values should be + // protected from eviction. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) { + candidate.nKeyedNetGroup = number_of_nodes - candidate.id; + }, + {0, 1, 2, 3}, random_context)); + + // Eight nodes with the lowest minimum ping time should be protected + // from eviction. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [](NodeEvictionCandidate& candidate) { + candidate.nMinPingUsecTime = candidate.id; + }, + {0, 1, 2, 3, 4, 5, 6, 7}, random_context)); + + // Four nodes that most recently sent us novel transactions accepted + // into our mempool should be protected from eviction. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) { + candidate.nLastTXTime = number_of_nodes - candidate.id; + }, + {0, 1, 2, 3}, random_context)); + + // Up to eight non-tx-relay peers that most recently sent us novel + // blocks should be protected from eviction. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) { + candidate.nLastBlockTime = number_of_nodes - candidate.id; + if (candidate.id <= 7) { + candidate.fRelayTxes = false; + candidate.fRelevantServices = true; + } + }, + {0, 1, 2, 3, 4, 5, 6, 7}, random_context)); + + // Four peers that most recently sent us novel blocks should be + // protected from eviction. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) { + candidate.nLastBlockTime = number_of_nodes - candidate.id; + }, + {0, 1, 2, 3}, random_context)); + + // Combination of the previous two tests. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) { + candidate.nLastBlockTime = number_of_nodes - candidate.id; + if (candidate.id <= 7) { + candidate.fRelayTxes = false; + candidate.fRelevantServices = true; + } + }, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context)); + + // Combination of all tests above. + BOOST_CHECK(!IsEvicted( + number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) { + candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected + candidate.nMinPingUsecTime = candidate.id; // 8 protected + candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected + candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected + }, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context)); + + // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most + // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay + // peers by last novel block time, and four more peers by last novel block time. + if (number_of_nodes >= 29) { + BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context))); + } + + // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least + // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last + // novel block time. + if (number_of_nodes <= 20) { + BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context))); + } + + // Cases left to test: + // * "Protect the half of the remaining nodes which have been connected the longest. [...]" + // * "Pick out up to 1/4 peers that are localhost, sorted by longest uptime. [...]" + // * "If any remaining peers are preferred for eviction consider only them. [...]" + // * "Identify the network group with the most connections and youngest member. [...]" + } + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index ac4db3a5b6..66ad7bb5ea 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -226,8 +226,22 @@ BOOST_AUTO_TEST_CASE(subnet_test) // 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()); + + // Create Non-IP subnets. + + const CNetAddr tor_addr{ + ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")}; + + subnet = CSubNet(tor_addr); + BOOST_CHECK(subnet.IsValid()); + BOOST_CHECK_EQUAL(subnet.ToString(), tor_addr.ToString()); + BOOST_CHECK(subnet.Match(tor_addr)); + BOOST_CHECK( + !subnet.Match(ResolveIP("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion"))); + BOOST_CHECK(!subnet.Match(ResolveIP("1.2.3.4"))); + + BOOST_CHECK(!CSubNet(tor_addr, 200).IsValid()); + BOOST_CHECK(!CSubNet(tor_addr, 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"); @@ -442,8 +456,7 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters) BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0"s, ret)); BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com"s, ret)); BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com\0"s, ret)); - // We only do subnetting for IPv4 and IPv6 - BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret)); + BOOST_CHECK(LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret)); BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0"s, ret)); BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com"s, ret)); BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com\0"s, ret)); diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index 1d7f4861fb..d5a4d3fd80 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp index 8c2712f764..c489ac04e9 100644 --- a/src/test/raii_event_tests.cpp +++ b/src/test/raii_event_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp index a42608a66d..7da364d316 100644 --- a/src/test/reverselock_tests.cpp +++ b/src/test/reverselock_tests.cpp @@ -48,12 +48,14 @@ BOOST_AUTO_TEST_CASE(reverselock_errors) WAIT_LOCK(mutex, lock); #ifdef DEBUG_LOCKORDER + bool prev = g_debug_lockorder_abort; + g_debug_lockorder_abort = false; + // Make sure trying to reverse lock a previous lock fails - try { - REVERSE_LOCK(lock2); - BOOST_CHECK(false); // REVERSE_LOCK(lock2) succeeded - } catch(...) { } + BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock2), std::logic_error, HasReason("lock2 was not most recent critical section locked")); BOOST_CHECK(lock2.owns_lock()); + + g_debug_lockorder_abort = prev; #endif // Make sure trying to reverse lock an unlocked lock fails diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp index 9a490aaf6b..740b2c72db 100644 --- a/src/test/sanity_tests.cpp +++ b/src/test/sanity_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 2e5a7549b7..d57c000b92 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -7,9 +7,11 @@ #include <util/time.h> #include <boost/test/unit_test.hpp> -#include <boost/thread/thread.hpp> +#include <functional> #include <mutex> +#include <thread> +#include <vector> BOOST_AUTO_TEST_SUITE(scheduler_tests) @@ -68,16 +70,16 @@ BOOST_AUTO_TEST_CASE(manythreads) BOOST_CHECK(last > now); // As soon as these are created they will start running and servicing the queue - boost::thread_group microThreads; + std::vector<std::thread> microThreads; for (int i = 0; i < 5; i++) - microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); + microThreads.emplace_back(std::bind(&CScheduler::serviceQueue, µTasks)); UninterruptibleSleep(std::chrono::microseconds{600}); now = std::chrono::system_clock::now(); // More threads and more tasks: for (int i = 0; i < 5; i++) - microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); + microThreads.emplace_back(std::bind(&CScheduler::serviceQueue, µTasks)); for (int i = 0; i < 100; i++) { std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); @@ -90,7 +92,10 @@ BOOST_AUTO_TEST_CASE(manythreads) // Drain the task queue then exit threads microTasks.StopWhenDrained(); - microThreads.join_all(); // ... wait until all the threads are done + // wait until all the threads are done + for (auto& thread: microThreads) { + if (thread.joinable()) thread.join(); + } int counterSum = 0; for (int i = 0; i < 10; i++) { @@ -130,9 +135,9 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) // if the queues only permit execution of one task at once then // the extra threads should effectively be doing nothing // if they don't we'll get out of order behaviour - boost::thread_group threads; + std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { - threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); + threads.emplace_back(std::bind(&CScheduler::serviceQueue, &scheduler)); } // these are not atomic, if SinglethreadedSchedulerClient prevents @@ -156,7 +161,9 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) // finish up scheduler.StopWhenDrained(); - threads.join_all(); + for (auto& thread: threads) { + if (thread.joinable()) thread.join(); + } BOOST_CHECK_EQUAL(counter1, 100); BOOST_CHECK_EQUAL(counter2, 100); diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 1d6bcadf69..366385b619 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -183,23 +183,23 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) s.clear(); s << ToByteVector(pubkey) << OP_CHECKSIG; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<PKHash>(&address) && - *boost::get<PKHash>(&address) == PKHash(pubkey)); + BOOST_CHECK(std::get_if<PKHash>(&address) && + *std::get_if<PKHash>(&address) == PKHash(pubkey)); // TxoutType::PUBKEYHASH s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<PKHash>(&address) && - *boost::get<PKHash>(&address) == PKHash(pubkey)); + BOOST_CHECK(std::get_if<PKHash>(&address) && + *std::get_if<PKHash>(&address) == PKHash(pubkey)); // TxoutType::SCRIPTHASH CScript redeemScript(s); // initialize with leftover P2PKH script s.clear(); s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<ScriptHash>(&address) && - *boost::get<ScriptHash>(&address) == ScriptHash(redeemScript)); + BOOST_CHECK(std::get_if<ScriptHash>(&address) && + *std::get_if<ScriptHash>(&address) == ScriptHash(redeemScript)); // TxoutType::MULTISIG s.clear(); @@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) BOOST_CHECK(ExtractDestination(s, address)); WitnessV0KeyHash keyhash; CHash160().Write(pubkey).Finalize(keyhash); - BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash); + BOOST_CHECK(std::get_if<WitnessV0KeyHash>(&address) && *std::get_if<WitnessV0KeyHash>(&address) == keyhash); // TxoutType::WITNESS_V0_SCRIPTHASH s.clear(); @@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin()); s << OP_0 << ToByteVector(scripthash); BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash); + BOOST_CHECK(std::get_if<WitnessV0ScriptHash>(&address) && *std::get_if<WitnessV0ScriptHash>(&address) == scripthash); // TxoutType::WITNESS_UNKNOWN with unknown version s.clear(); @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) unk.length = 33; unk.version = 1; std::copy(pubkey.begin(), pubkey.end(), unk.program); - BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk); + BOOST_CHECK(std::get_if<WitnessUnknown>(&address) && *std::get_if<WitnessUnknown>(&address) == unk); } BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) @@ -259,8 +259,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get<PKHash>(&addresses[0]) && - *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) && + *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); // TxoutType::PUBKEYHASH s.clear(); @@ -269,8 +269,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get<PKHash>(&addresses[0]) && - *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) && + *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); // TxoutType::SCRIPTHASH CScript redeemScript(s); // initialize with leftover P2PKH script @@ -280,8 +280,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) && - *boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript)); + BOOST_CHECK(std::get_if<ScriptHash>(&addresses[0]) && + *std::get_if<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript)); // TxoutType::MULTISIG s.clear(); @@ -293,10 +293,10 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG); BOOST_CHECK_EQUAL(addresses.size(), 2U); BOOST_CHECK_EQUAL(nRequired, 2); - BOOST_CHECK(boost::get<PKHash>(&addresses[0]) && - *boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); - BOOST_CHECK(boost::get<PKHash>(&addresses[1]) && - *boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) && + *std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(std::get_if<PKHash>(&addresses[1]) && + *std::get_if<PKHash>(&addresses[1]) == PKHash(pubkeys[1])); // TxoutType::NULL_DATA s.clear(); diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 26e24d836a..3079c9ff29 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp index 19029ebd3c..3e4d1dac9e 100644 --- a/src/test/sync_tests.cpp +++ b/src/test/sync_tests.cpp @@ -7,6 +7,8 @@ #include <boost/test/unit_test.hpp> +#include <mutex> + namespace { template <typename MutexType> void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2) @@ -29,6 +31,49 @@ void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2) BOOST_CHECK(!error_thrown); #endif } + +#ifdef DEBUG_LOCKORDER +template <typename MutexType> +void TestDoubleLock2(MutexType& m) +{ + ENTER_CRITICAL_SECTION(m); + LEAVE_CRITICAL_SECTION(m); +} + +template <typename MutexType> +void TestDoubleLock(bool should_throw) +{ + const bool prev = g_debug_lockorder_abort; + g_debug_lockorder_abort = false; + + MutexType m; + ENTER_CRITICAL_SECTION(m); + if (should_throw) { + BOOST_CHECK_EXCEPTION(TestDoubleLock2(m), std::logic_error, + HasReason("double lock detected")); + } else { + BOOST_CHECK_NO_THROW(TestDoubleLock2(m)); + } + LEAVE_CRITICAL_SECTION(m); + + BOOST_CHECK(LockStackEmpty()); + + g_debug_lockorder_abort = prev; +} +#endif /* DEBUG_LOCKORDER */ + +template <typename MutexType> +void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_THREAD_SAFETY_ANALYSIS +{ + ENTER_CRITICAL_SECTION(mutex1); + ENTER_CRITICAL_SECTION(mutex2); +#ifdef DEBUG_LOCKORDER + BOOST_CHECK_EXCEPTION(LEAVE_CRITICAL_SECTION(mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked")); +#endif // DEBUG_LOCKORDER + LEAVE_CRITICAL_SECTION(mutex2); + LEAVE_CRITICAL_SECTION(mutex1); + BOOST_CHECK(LockStackEmpty()); +} } // namespace BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup) @@ -55,4 +100,43 @@ BOOST_AUTO_TEST_CASE(potential_deadlock_detected) #endif } +/* Double lock would produce an undefined behavior. Thus, we only do that if + * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER + * build to produce tests that exhibit known undefined behavior. */ +#ifdef DEBUG_LOCKORDER +BOOST_AUTO_TEST_CASE(double_lock_mutex) +{ + TestDoubleLock<Mutex>(true /* should throw */); +} + +BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex) +{ + TestDoubleLock<RecursiveMutex>(false /* should not throw */); +} +#endif /* DEBUG_LOCKORDER */ + +BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected) +{ +#ifdef DEBUG_LOCKORDER + bool prev = g_debug_lockorder_abort; + g_debug_lockorder_abort = false; +#endif // DEBUG_LOCKORDER + + RecursiveMutex rmutex1, rmutex2; + TestInconsistentLockOrderDetected(rmutex1, rmutex2); + // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical) + // the lock tracking data must not have been broken by exception. + TestInconsistentLockOrderDetected(rmutex1, rmutex2); + + Mutex mutex1, mutex2; + TestInconsistentLockOrderDetected(mutex1, mutex2); + // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical) + // the lock tracking data must not have been broken by exception. + TestInconsistentLockOrderDetected(mutex1, mutex2); + +#ifdef DEBUG_LOCKORDER + g_debug_lockorder_abort = prev; +#endif // DEBUG_LOCKORDER +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index a55145c738..ce555f7299 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. // diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 1f520074b1..1eb30c68d6 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -24,6 +24,7 @@ #include <util/strencodings.h> #include <validation.h> +#include <functional> #include <map> #include <string> @@ -428,12 +429,10 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) // check all inputs concurrently, with the cache PrecomputedTransactionData txdata(tx); - boost::thread_group threadGroup; CCheckQueue<CScriptCheck> scriptcheckqueue(128); CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue); - for (int i=0; i<20; i++) - threadGroup.create_thread(std::bind(&CCheckQueue<CScriptCheck>::Thread, std::ref(scriptcheckqueue))); + scriptcheckqueue.StartWorkerThreads(20); std::vector<Coin> coins; for(uint32_t i = 0; i < mtx.vin.size(); i++) { @@ -455,9 +454,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) bool controlCheck = control.Wait(); assert(controlCheck); - - threadGroup.interrupt_all(); - threadGroup.join_all(); + scriptcheckqueue.StopWorkerThreads(); } SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutableTransaction& input2, const CTransactionRef tx) @@ -762,7 +759,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) // Only one TxoutType::NULL_DATA permitted in all cases t.vout.resize(2); t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[0].nValue = 0; t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[1].nValue = 0; reason.clear(); BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); BOOST_CHECK_EQUAL(reason, "multi-op-return"); diff --git a/src/test/util/logging.cpp b/src/test/util/logging.cpp index 65a64f2384..c1dd47f06c 100644 --- a/src/test/util/logging.cpp +++ b/src/test/util/logging.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/test/util/logging.h b/src/test/util/logging.h index 1fcf7ca305..ebe0ecf623 100644 --- a/src/test/util/logging.h +++ b/src/test/util/logging.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -32,7 +32,7 @@ class DebugLogHelper void check_found(); public: - DebugLogHelper(std::string message, MatchFn match = [](const std::string*){ return true; }); + explicit DebugLogHelper(std::string message, MatchFn match = [](const std::string*){ return true; }); ~DebugLogHelper() { check_found(); } }; diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp index 74536ae74c..0c6487fbfa 100644 --- a/src/test/util/mining.cpp +++ b/src/test/util/mining.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/test/util/net.h b/src/test/util/net.h index 1208e92762..e25036be26 100644 --- a/src/test/util/net.h +++ b/src/test/util/net.h @@ -30,4 +30,35 @@ struct ConnmanTestMsg : public CConnman { bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) const; }; +constexpr ServiceFlags ALL_SERVICE_FLAGS[]{ + NODE_NONE, + NODE_NETWORK, + NODE_BLOOM, + NODE_WITNESS, + NODE_COMPACT_FILTERS, + NODE_NETWORK_LIMITED, +}; + +constexpr NetPermissionFlags ALL_NET_PERMISSION_FLAGS[]{ + NetPermissionFlags::PF_NONE, + NetPermissionFlags::PF_BLOOMFILTER, + NetPermissionFlags::PF_RELAY, + NetPermissionFlags::PF_FORCERELAY, + NetPermissionFlags::PF_NOBAN, + NetPermissionFlags::PF_MEMPOOL, + NetPermissionFlags::PF_ADDR, + NetPermissionFlags::PF_DOWNLOAD, + NetPermissionFlags::PF_ISIMPLICIT, + NetPermissionFlags::PF_ALL, +}; + +constexpr ConnectionType ALL_CONNECTION_TYPES[]{ + ConnectionType::INBOUND, + ConnectionType::OUTBOUND_FULL_RELAY, + ConnectionType::MANUAL, + ConnectionType::FEELER, + ConnectionType::BLOCK_RELAY, + ConnectionType::ADDR_FETCH, +}; + #endif // BITCOIN_TEST_UTIL_NET_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 2d3137e1e2..b9f3f8c955 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -16,6 +16,7 @@ #include <net.h> #include <net_processing.h> #include <noui.h> +#include <policy/fees.h> #include <pow.h> #include <rpc/blockchain.h> #include <rpc/register.h> @@ -124,27 +125,53 @@ BasicTestingSetup::~BasicTestingSetup() ECC_Stop(); } -TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args) +ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args) : BasicTestingSetup(chainName, extra_args) { - const CChainParams& chainparams = Params(); - // Ideally we'd move all the RPC tests to the functional testing framework - // instead of unit tests, but for now we need these here. - RegisterAllCoreRPCCommands(tableRPC); - - m_node.scheduler = MakeUnique<CScheduler>(); - // We have to run a scheduler thread to prevent ActivateBestChain // from blocking due to queue overrun. - threadGroup.create_thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); }); + m_node.scheduler = MakeUnique<CScheduler>(); + m_node.scheduler->m_service_thread = std::thread([&] { TraceThread("scheduler", [&] { m_node.scheduler->serviceQueue(); }); }); GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler); pblocktree.reset(new CBlockTreeDB(1 << 20, true)); - m_node.mempool = MakeUnique<CTxMemPool>(&::feeEstimator); - m_node.mempool->setSanityCheck(1.0); + m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(); + m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1); m_node.chainman = &::g_chainman; + + // Start script-checking threads. Set g_parallel_script_checks to true so they are used. + constexpr int script_check_threads = 2; + StartScriptCheckWorkerThreads(script_check_threads); + g_parallel_script_checks = true; +} + +ChainTestingSetup::~ChainTestingSetup() +{ + if (m_node.scheduler) m_node.scheduler->stop(); + StopScriptCheckWorkerThreads(); + GetMainSignals().FlushBackgroundCallbacks(); + GetMainSignals().UnregisterBackgroundSignalScheduler(); + m_node.connman.reset(); + m_node.banman.reset(); + m_node.args = 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; + pblocktree.reset(); +} + +TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const char*>& extra_args) + : ChainTestingSetup(chainName, extra_args) +{ + const CChainParams& chainparams = Params(); + // Ideally we'd move all the RPC tests to the functional testing framework + // instead of unit tests, but for now we need these here. + RegisterAllCoreRPCCommands(tableRPC); + m_node.chainman->InitializeChainstate(*m_node.mempool); ::ChainstateActive().InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); @@ -156,20 +183,15 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const } BlockValidationState state; - if (!ActivateBestChain(state, chainparams)) { + if (!::ChainstateActive().ActivateBestChain(state, chainparams)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString())); } - // Start script-checking threads. Set g_parallel_script_checks to true so they are used. - constexpr int script_check_threads = 2; - for (int i = 0; i < script_check_threads; ++i) { - threadGroup.create_thread([i]() { return ThreadScriptCheck(i); }); - } - g_parallel_script_checks = true; - 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.peerman = MakeUnique<PeerManager>(chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool); + m_node.peerman = PeerManager::make(chainparams, *m_node.connman, m_node.banman.get(), + *m_node.scheduler, *m_node.chainman, *m_node.mempool, + false); { CConnman::Options options; options.m_msgproc = m_node.peerman.get(); @@ -177,24 +199,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const } } -TestingSetup::~TestingSetup() -{ - if (m_node.scheduler) m_node.scheduler->stop(); - threadGroup.interrupt_all(); - threadGroup.join_all(); - GetMainSignals().FlushBackgroundCallbacks(); - GetMainSignals().UnregisterBackgroundSignalScheduler(); - m_node.connman.reset(); - m_node.banman.reset(); - m_node.args = 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; - pblocktree.reset(); -} - TestChain100Setup::TestChain100Setup() { // Generate a 100-block chain: @@ -232,12 +236,12 @@ TestChain100Setup::~TestChain100Setup() gArgs.ForceSetArg("-segwitheight", "0"); } -CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const { return FromTx(MakeTransactionRef(tx)); } -CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) const { return CTxMemPoolEntry(tx, nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp); diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 1812ce1666..331c1235cb 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -17,8 +17,7 @@ #include <util/string.h> #include <type_traits> - -#include <boost/thread/thread.hpp> +#include <vector> /** This is connected to the logger. Can be used to redirect logs to any other log */ extern const std::function<void(const std::string&)> G_TEST_LOG_FUN; @@ -83,14 +82,20 @@ private: const fs::path m_path_root; }; -/** Testing setup that configures a complete environment. - * Included are coins database, script check threads setup. +/** Testing setup that performs all steps up until right before + * ChainstateManager gets initialized. Meant for testing ChainstateManager + * initialization behaviour. */ -struct TestingSetup : public BasicTestingSetup { - boost::thread_group threadGroup; +struct ChainTestingSetup : public BasicTestingSetup { + + explicit ChainTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {}); + ~ChainTestingSetup(); +}; +/** Testing setup that configures a complete environment. + */ +struct TestingSetup : public ChainTestingSetup { explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::vector<const char*>& extra_args = {}); - ~TestingSetup(); }; /** Identical to TestingSetup, but chain set to regtest */ @@ -138,8 +143,8 @@ struct TestMemPoolEntryHelper nFee(0), nTime(0), nHeight(1), spendsCoinbase(false), sigOpCost(4) { } - CTxMemPoolEntry FromTx(const CMutableTransaction& tx); - CTxMemPoolEntry FromTx(const CTransactionRef& tx); + CTxMemPoolEntry FromTx(const CMutableTransaction& tx) const; + CTxMemPoolEntry FromTx(const CTransactionRef& tx) const; // Change the default value TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 8f2fc71cce..4133f2623b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -231,7 +231,7 @@ public: Optional<std::vector<std::string>> list_value; const char* error = nullptr; - Expect(util::SettingsValue s) : setting(std::move(s)) {} + explicit Expect(util::SettingsValue s) : setting(std::move(s)) {} Expect& DefaultString() { default_string = true; return *this; } Expect& DefaultInt() { default_int = true; return *this; } Expect& DefaultBool() { default_bool = true; return *this; } @@ -1423,10 +1423,18 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) BOOST_CHECK(ParseInt32("2147483647", &n) && n == 2147483647); BOOST_CHECK(ParseInt32("-2147483648", &n) && n == (-2147483647 - 1)); // (-2147483647 - 1) equals INT_MIN BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234); + BOOST_CHECK(ParseInt32("00000000000000001234", &n) && n == 1234); + BOOST_CHECK(ParseInt32("-00000000000000001234", &n) && n == -1234); + BOOST_CHECK(ParseInt32("00000000000000000000", &n) && n == 0); + BOOST_CHECK(ParseInt32("-00000000000000000000", &n) && n == 0); // Invalid values BOOST_CHECK(!ParseInt32("", &n)); BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside BOOST_CHECK(!ParseInt32("1 ", &n)); + BOOST_CHECK(!ParseInt32("++1", &n)); + BOOST_CHECK(!ParseInt32("+-1", &n)); + BOOST_CHECK(!ParseInt32("-+1", &n)); + BOOST_CHECK(!ParseInt32("--1", &n)); BOOST_CHECK(!ParseInt32("1a", &n)); BOOST_CHECK(!ParseInt32("aap", &n)); BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex @@ -1482,10 +1490,19 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt32) BOOST_CHECK(ParseUInt32("2147483647", &n) && n == 2147483647); BOOST_CHECK(ParseUInt32("2147483648", &n) && n == (uint32_t)2147483648); BOOST_CHECK(ParseUInt32("4294967295", &n) && n == (uint32_t)4294967295); + BOOST_CHECK(ParseUInt32("+1234", &n) && n == 1234); + BOOST_CHECK(ParseUInt32("00000000000000001234", &n) && n == 1234); + BOOST_CHECK(ParseUInt32("00000000000000000000", &n) && n == 0); // Invalid values + BOOST_CHECK(!ParseUInt32("-00000000000000000000", &n)); BOOST_CHECK(!ParseUInt32("", &n)); BOOST_CHECK(!ParseUInt32(" 1", &n)); // no padding inside BOOST_CHECK(!ParseUInt32(" -1", &n)); + BOOST_CHECK(!ParseUInt32("++1", &n)); + BOOST_CHECK(!ParseUInt32("+-1", &n)); + BOOST_CHECK(!ParseUInt32("-+1", &n)); + BOOST_CHECK(!ParseUInt32("--1", &n)); + BOOST_CHECK(!ParseUInt32("-1", &n)); BOOST_CHECK(!ParseUInt32("1 ", &n)); BOOST_CHECK(!ParseUInt32("1a", &n)); BOOST_CHECK(!ParseUInt32("aap", &n)); @@ -1843,7 +1860,7 @@ BOOST_AUTO_TEST_CASE(test_Capitalize) BOOST_CHECK_EQUAL(Capitalize("\x00\xfe\xff"), "\x00\xfe\xff"); } -static std::string SpanToStr(Span<const char>& span) +static std::string SpanToStr(const Span<const char>& span) { return std::string(span.begin(), span.end()); } @@ -1997,12 +2014,6 @@ struct Tracker copies = t.copies + 1; return *this; } - Tracker& operator=(Tracker&& t) noexcept - { - origin = t.origin; - copies = t.copies; - return *this; - } }; } diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index ea17cb50f1..ec45d9a434 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -95,8 +95,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash) std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock) { - LOCK(cs_main); // For LookupBlockIndex - GenerateCoinbaseCommitment(*pblock, LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus()); + LOCK(cs_main); // For g_chainman.m_blockman.LookupBlockIndex + GenerateCoinbaseCommitment(*pblock, g_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus()); pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp index c8a375275f..92d8cf2e7d 100644 --- a/src/test/validation_chainstate_tests.cpp +++ b/src/test/validation_chainstate_tests.cpp @@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches) return outp; }; - CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool)); + 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 36badafc4e..3d8570e27c 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -15,21 +15,22 @@ #include <boost/test/unit_test.hpp> -BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup) +BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup) //! Basic tests for ChainstateManager. //! //! First create a legacy (IBD) chainstate, then create a snapshot chainstate. BOOST_AUTO_TEST_CASE(chainstatemanager) { - ChainstateManager manager; - CTxMemPool mempool; + ChainstateManager& manager = *m_node.chainman; + CTxMemPool& mempool = *m_node.mempool; + std::vector<CChainState*> chainstates; const CChainParams& chainparams = Params(); // Create a legacy (IBD) chainstate. // - CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool)); + 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); @@ -55,7 +56,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) // Create a snapshot-based chainstate. // - CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool, 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,8 +105,9 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) //! Test rebalancing the caches associated with each chainstate. BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) { - ChainstateManager manager; - CTxMemPool mempool; + ChainstateManager& manager = *m_node.chainman; + CTxMemPool& mempool = *m_node.mempool; + size_t max_cache = 10000; manager.m_total_coinsdb_cache = max_cache; manager.m_total_coinstip_cache = max_cache; @@ -114,7 +116,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) // Create a legacy (IBD) chainstate. // - CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool)); + 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); @@ -122,6 +124,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) { LOCK(::cs_main); c1.InitCoinsCache(1 << 23); + BOOST_REQUIRE(c1.LoadGenesisBlock(Params())); c1.CoinsTip().SetBestBlock(InsecureRand256()); manager.MaybeRebalanceCaches(); } @@ -131,7 +134,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) // Create a snapshot-based chainstate. // - CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool, 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); @@ -139,6 +142,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) { LOCK(::cs_main); c2.InitCoinsCache(1 << 23); + BOOST_REQUIRE(c2.LoadGenesisBlock(Params())); c2.CoinsTip().SetBestBlock(InsecureRand256()); manager.MaybeRebalanceCaches(); } diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp index c3816af0cd..9e37f14921 100644 --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 The Bitcoin Core developers +// Copyright (c) 2014-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(signet_parse_tests) 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 + block.vtx.push_back(MakeTransactionRef(cb)); // Add dummy tx to exercise merkle root code BOOST_CHECK(!SignetTxs::Create(block, challenge)); BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus())); diff --git a/src/threadsafety.h b/src/threadsafety.h index 52bf83b676..28b6177927 100644 --- a/src/threadsafety.h +++ b/src/threadsafety.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/tinyformat.h b/src/tinyformat.h index be63f2d5d8..bc893ccda5 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -514,7 +514,7 @@ class FormatArg { } template<typename T> - FormatArg(const T& value) + explicit FormatArg(const T& value) : m_value(static_cast<const void*>(&value)), m_formatImpl(&formatImpl<T>), m_toIntImpl(&toIntImpl<T>) @@ -970,7 +970,7 @@ class FormatListN : public FormatList public: #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template<typename... Args> - FormatListN(const Args&... args) + explicit FormatListN(const Args&... args) : FormatList(&m_formatterStore[0], N), m_formatterStore { FormatArg(args)... } { static_assert(sizeof...(args) == N, "Number of args must be N"); } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 8ebe3d750d..908ad35e1b 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Copyright (c) 2017 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,6 +7,7 @@ #include <chainparams.h> #include <chainparamsbase.h> +#include <compat.h> #include <crypto/hmac_sha256.h> #include <net.h> #include <netaddress.h> @@ -14,10 +15,11 @@ #include <util/strencodings.h> #include <util/system.h> -#include <vector> #include <deque> +#include <functional> #include <set> #include <stdlib.h> +#include <vector> #include <boost/signals2/signal.hpp> #include <boost/algorithm/string/split.hpp> diff --git a/src/torcontrol.h b/src/torcontrol.h index 71a6960e54..00f19db6ae 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 0c2b731967..c370f9e981 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -331,15 +331,10 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, assert(int(nSigOpCostWithAncestors) >= 0); } -CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) - : nTransactionsUpdated(0), minerPolicyEstimator(estimator), m_epoch(0), m_has_epoch_guard(false) +CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator, int check_ratio) + : m_check_ratio(check_ratio), minerPolicyEstimator(estimator) { _clear(); //lock free clear - - // Sanity checks off by default for performance, because otherwise - // accepting transactions becomes O(N^2) where N is the number - // of transactions in the pool - nCheckFrequency = 0; } bool CTxMemPool::isSpent(const COutPoint& outpoint) const @@ -523,7 +518,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem if (it2 != mapTx.end()) continue; const Coin &coin = pcoins->AccessCoin(txin.prevout); - if (nCheckFrequency != 0) assert(!coin.IsSpent()); + if (m_check_ratio != 0) assert(!coin.IsSpent()); if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) { txToRemove.insert(it); break; @@ -619,20 +614,19 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m void CTxMemPool::check(const CCoinsViewCache *pcoins) const { - LOCK(cs); - if (nCheckFrequency == 0) - return; + if (m_check_ratio == 0) return; - if (GetRand(std::numeric_limits<uint32_t>::max()) >= nCheckFrequency) - return; + if (GetRand(m_check_ratio) >= 1) return; + AssertLockHeld(::cs_main); + LOCK(cs); LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); uint64_t checkTotal = 0; uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins)); - const int64_t spendheight = GetSpendHeight(mempoolDuplicate); + const int64_t spendheight = g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate); std::list<const CTxMemPoolEntry*> waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { @@ -1134,5 +1128,3 @@ CTxMemPool::EpochGuard::~EpochGuard() ++pool.m_epoch; pool.m_has_epoch_guard = false; } - -SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} diff --git a/src/txmempool.h b/src/txmempool.h index f513f14af6..0a9cd81ff5 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -15,13 +15,13 @@ #include <amount.h> #include <coins.h> -#include <crypto/siphash.h> #include <indirectmap.h> #include <optional.h> #include <policy/feerate.h> #include <primitives/transaction.h> #include <sync.h> #include <random.h> +#include <util/hasher.h> #include <boost/multi_index_container.hpp> #include <boost/multi_index/hashed_index.hpp> @@ -398,20 +398,6 @@ enum class MemPoolRemovalReason { REPLACED, //!< Removed for replacement }; -class SaltedTxidHasher -{ -private: - /** Salt */ - const uint64_t k0, k1; - -public: - SaltedTxidHasher(); - - size_t operator()(const uint256& txid) const { - return SipHashUint256(k0, k1, txid); - } -}; - /** * CTxMemPool stores valid-according-to-the-current-best-chain transactions * that may be included in the next block. @@ -488,8 +474,8 @@ public: class CTxMemPool { private: - uint32_t nCheckFrequency GUARDED_BY(cs); //!< Value n means that n times in 2^32 we check. - std::atomic<unsigned int> nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation + const int m_check_ratio; //!< Value n means that 1 times in n we check. + std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* minerPolicyEstimator; uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141. @@ -498,8 +484,8 @@ private: mutable int64_t lastRollingFeeUpdate; mutable bool blockSinceLastRollingFeeBump; mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially - mutable uint64_t m_epoch; - mutable bool m_has_epoch_guard; + mutable uint64_t m_epoch{0}; + mutable bool m_has_epoch_guard{false}; // In-memory counter for external mempool tracking purposes. // This number is incremented once every time a transaction @@ -601,8 +587,14 @@ public: std::map<uint256, CAmount> mapDeltas; /** Create a new CTxMemPool. + * Sanity checks will be off by default for performance, because otherwise + * accepting transactions becomes O(N^2) where N is the number of transactions + * in the pool. + * + * @param[in] estimator is used to estimate appropriate transaction fees. + * @param[in] check_ratio is the ratio used to determine how often sanity checks will run. */ - explicit CTxMemPool(CBlockPolicyEstimator* estimator = nullptr); + explicit CTxMemPool(CBlockPolicyEstimator* estimator = nullptr, int check_ratio = 0); /** * If sanity-checking is turned on, check makes sure the pool is @@ -610,8 +602,7 @@ public: * all inputs are in the mapNextTx array). If sanity-checking is turned off, * check does nothing. */ - void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(double dFrequency = 1.0) { LOCK(cs); nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); } + void check(const CCoinsViewCache *pcoins) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of @@ -850,7 +841,7 @@ public: class EpochGuard { const CTxMemPool& pool; public: - EpochGuard(const CTxMemPool& in); + explicit EpochGuard(const CTxMemPool& in); ~EpochGuard(); }; // N.B. GetFreshEpoch modifies mutable state via the EpochGuard construction diff --git a/src/txrequest.cpp b/src/txrequest.cpp index 09eb78e927..e54c073328 100644 --- a/src/txrequest.cpp +++ b/src/txrequest.cpp @@ -170,7 +170,7 @@ using ByTxHashView = std::tuple<const uint256&, State, Priority>; class ByTxHashViewExtractor { const PriorityComputer& m_computer; public: - ByTxHashViewExtractor(const PriorityComputer& computer) : m_computer(computer) {} + explicit ByTxHashViewExtractor(const PriorityComputer& computer) : m_computer(computer) {} using result_type = ByTxHashView; result_type operator()(const Announcement& ann) const { @@ -522,7 +522,7 @@ private: } public: - Impl(bool deterministic) : + explicit 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( diff --git a/src/uint256.cpp b/src/uint256.cpp index f358b62903..cd9cbb566a 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/uint256.h b/src/uint256.h index ceae70707e..fadf2320af 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/util/asmap.h b/src/util/asmap.h index b31e639bb5..d0588bc8c3 100644 --- a/src/util/asmap.h +++ b/src/util/asmap.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/util/bip32.h b/src/util/bip32.h index 347e83db9e..8f86f2aaa6 100644 --- a/src/util/bip32.h +++ b/src/util/bip32.h @@ -10,7 +10,7 @@ #include <vector> /** Parse an HD keypaths like "m/7/0'/2000". */ -NODISCARD bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath); +[[nodiscard]] bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath); /** Write HD keypaths as strings */ std::string WriteHDKeypath(const std::vector<uint32_t>& keypath); diff --git a/src/util/check.h b/src/util/check.h index 9edf394492..bc62da3440 100644 --- a/src/util/check.h +++ b/src/util/check.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. @@ -46,7 +46,7 @@ class NonFatalCheckError : public std::runtime_error #error "Cannot compile without assertions!" #endif -/** Helper for Assert(). TODO remove in C++14 and replace `decltype(get_pure_r_value(val))` with `T` (templated lambda) */ +/** Helper for Assert() */ template <typename T> T get_pure_r_value(T&& val) { @@ -54,6 +54,22 @@ T get_pure_r_value(T&& val) } /** Identity function. Abort if the value compares equal to zero */ -#define Assert(val) [&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); assert(#val && check); return std::forward<decltype(get_pure_r_value(val))>(check); }() +#define Assert(val) ([&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); assert(#val && check); return std::forward<decltype(get_pure_r_value(val))>(check); }()) + +/** + * Assume is the identity function. + * + * - Should be used to run non-fatal checks. In debug builds it behaves like + * Assert()/assert() to notify developers and testers about non-fatal errors. + * In production it doesn't warn or log anything. + * - For fatal errors, use Assert(). + * - For non-fatal errors in interactive sessions (e.g. RPC or command line + * interfaces), CHECK_NONFATAL() might be more appropriate. + */ +#ifdef ABORT_ON_FAILED_ASSUME +#define Assume(val) Assert(val) +#else +#define Assume(val) ((void)(val)) +#endif #endif // BITCOIN_UTIL_CHECK_H diff --git a/src/util/error.cpp b/src/util/error.cpp index 6c94b80683..76fac4d391 100644 --- a/src/util/error.cpp +++ b/src/util/error.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2019 The Bitcoin Core developers +// 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. diff --git a/src/util/error.h b/src/util/error.h index b9830c9eea..6633498d2b 100644 --- a/src/util/error.h +++ b/src/util/error.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2019 The Bitcoin Core developers +// 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. diff --git a/src/util/fees.cpp b/src/util/fees.cpp index 1855c0bc90..cbefe18dbb 100644 --- a/src/util/fees.cpp +++ b/src/util/fees.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/util/fees.h b/src/util/fees.h index 3f1c33ad9c..9ef2389d3e 100644 --- a/src/util/fees.h +++ b/src/util/fees.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_UTIL_FEES_H diff --git a/src/util/golombrice.h b/src/util/golombrice.h index 425e7f6681..67d262406f 100644 --- a/src/util/golombrice.h +++ b/src/util/golombrice.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/util/hasher.cpp b/src/util/hasher.cpp new file mode 100644 index 0000000000..5900daf050 --- /dev/null +++ b/src/util/hasher.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <random.h> +#include <util/hasher.h> + +#include <limits> + +SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} + +SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} + +SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand(std::numeric_limits<uint64_t>::max())), m_k1(GetRand(std::numeric_limits<uint64_t>::max())) {} + +size_t SaltedSipHasher::operator()(const Span<const unsigned char>& script) const +{ + return CSipHasher(m_k0, m_k1).Write(script.data(), script.size()).Finalize(); +} diff --git a/src/util/hasher.h b/src/util/hasher.h new file mode 100644 index 0000000000..fa2fea30d8 --- /dev/null +++ b/src/util/hasher.h @@ -0,0 +1,99 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_HASHER_H +#define BITCOIN_UTIL_HASHER_H + +#include <crypto/siphash.h> +#include <primitives/transaction.h> +#include <uint256.h> + +class SaltedTxidHasher +{ +private: + /** Salt */ + const uint64_t k0, k1; + +public: + SaltedTxidHasher(); + + size_t operator()(const uint256& txid) const { + return SipHashUint256(k0, k1, txid); + } +}; + +class SaltedOutpointHasher +{ +private: + /** Salt */ + const uint64_t k0, k1; + +public: + SaltedOutpointHasher(); + + /** + * This *must* return size_t. With Boost 1.46 on 32-bit systems the + * unordered_map will behave unpredictably if the custom hasher returns a + * uint64_t, resulting in failures when syncing the chain (#4634). + * + * Having the hash noexcept allows libstdc++'s unordered_map to recalculate + * the hash during rehash, so it does not have to cache the value. This + * reduces node's memory by sizeof(size_t). The required recalculation has + * a slight performance penalty (around 1.6%), but this is compensated by + * memory savings of about 9% which allow for a larger dbcache setting. + * + * @see https://gcc.gnu.org/onlinedocs/gcc-9.2.0/libstdc++/manual/manual/unordered_associative.html + */ + size_t operator()(const COutPoint& id) const noexcept { + return SipHashUint256Extra(k0, k1, id.hash, id.n); + } +}; + +struct FilterHeaderHasher +{ + size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } +}; + +/** + * We're hashing a nonce into the entries themselves, so we don't need extra + * blinding in the set hash computation. + * + * This may exhibit platform endian dependent behavior but because these are + * nonced hashes (random) and this state is only ever used locally it is safe. + * All that matters is local consistency. + */ +class SignatureCacheHasher +{ +public: + template <uint8_t hash_select> + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin()+4*hash_select, 4); + return u; + } +}; + +struct BlockHasher +{ + // this used to call `GetCheapHash()` in uint256, which was later moved; the + // cheap hash function simply calls ReadLE64() however, so the end result is + // identical + size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } +}; + +class SaltedSipHasher +{ +private: + /** Salt */ + const uint64_t m_k0, m_k1; + +public: + SaltedSipHasher(); + + size_t operator()(const Span<const unsigned char>& script) const; +}; + +#endif // BITCOIN_UTIL_HASHER_H diff --git a/src/util/memory.h b/src/util/memory.h index 4d73b32869..f21b81bade 100644 --- a/src/util/memory.h +++ b/src/util/memory.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/util/message.cpp b/src/util/message.cpp index e1d5cff48c..73948e4ff1 100644 --- a/src/util/message.cpp +++ b/src/util/message.cpp @@ -31,7 +31,7 @@ MessageVerificationResult MessageVerify( return MessageVerificationResult::ERR_INVALID_ADDRESS; } - if (boost::get<PKHash>(&destination) == nullptr) { + if (std::get_if<PKHash>(&destination) == nullptr) { return MessageVerificationResult::ERR_ADDRESS_NO_KEY; } diff --git a/src/util/moneystr.h b/src/util/moneystr.h index 9d2b6da0fc..da7f673cda 100644 --- a/src/util/moneystr.h +++ b/src/util/moneystr.h @@ -19,6 +19,6 @@ */ std::string FormatMoney(const CAmount& n); /** Parse an amount denoted in full coins. E.g. "0.0034" supplied on the command line. **/ -NODISCARD bool ParseMoney(const std::string& str, CAmount& nRet); +[[nodiscard]] bool ParseMoney(const std::string& str, CAmount& nRet); #endif // BITCOIN_UTIL_MONEYSTR_H diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 3236184b0b..f3d54a2ac9 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -280,7 +280,7 @@ std::string DecodeBase32(const std::string& str, bool* pf_invalid) return std::string((const char*)vchRet.data(), vchRet.size()); } -NODISCARD static bool ParsePrechecks(const std::string& str) +[[nodiscard]] static bool ParsePrechecks(const std::string& str) { if (str.empty()) // No empty string allowed return false; diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 1a217dd12d..b4a61202ef 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -101,42 +101,42 @@ constexpr inline bool IsSpace(char c) noexcept { * @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 ParseInt32(const std::string& str, int32_t *out); +[[nodiscard]] bool ParseInt32(const std::string& str, int32_t *out); /** * Convert string to signed 64-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 ParseInt64(const std::string& str, int64_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); +[[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. */ -NODISCARD bool ParseUInt32(const std::string& str, uint32_t *out); +[[nodiscard]] bool ParseUInt32(const std::string& str, uint32_t *out); /** * Convert decimal string to unsigned 64-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 ParseUInt64(const std::string& str, uint64_t *out); +[[nodiscard]] bool ParseUInt64(const std::string& str, uint64_t *out); /** * Convert string to double with strict parse error feedback. * @returns true if the entire string could be parsed as valid double, * false if not the entire string could be parsed or when overflow or underflow occurred. */ -NODISCARD bool ParseDouble(const std::string& str, double *out); +[[nodiscard]] bool ParseDouble(const std::string& str, double *out); /** * Convert a span of bytes to a lower-case hexadecimal string. @@ -166,11 +166,11 @@ bool TimingResistantEqual(const T& a, const T& b) } /** Parse number as fixed point according to JSON number syntax. - * See http://json.org/number.gif + * See https://json.org/number.gif * @returns true on success, false on error. * @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger. */ -NODISCARD bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +[[nodiscard]] bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); /** Convert from one power-of-2 number base to another. */ template<int frombits, int tobits, bool pad, typename O, typename I> diff --git a/src/util/string.h b/src/util/string.h index a0c87bd00c..5ffdc80d88 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -15,7 +15,7 @@ #include <string> #include <vector> -NODISCARD inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v") +[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v") { std::string::size_type front = str.find_first_not_of(pattern); if (front == std::string::npos) { @@ -59,7 +59,7 @@ inline std::string Join(const std::vector<std::string>& list, const std::string& /** * Check if a string does not contain any embedded NUL (\0) characters */ -NODISCARD inline bool ValidAsCString(const std::string& str) noexcept +[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept { return str.size() == strlen(str.c_str()); } @@ -80,7 +80,7 @@ std::string ToString(const T& t) * Check whether a container begins with the given prefix. */ template <typename T1, size_t PREFIX_LEN> -NODISCARD inline bool HasPrefix(const T1& obj, +[[nodiscard]] inline bool HasPrefix(const T1& obj, const std::array<uint8_t, PREFIX_LEN>& prefix) { return obj.size() >= PREFIX_LEN && diff --git a/src/util/system.cpp b/src/util/system.cpp index 5f30136fa2..d1fb921642 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -398,7 +398,7 @@ bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const } if (filepath) { std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME); - *filepath = fs::absolute(temp ? settings + ".tmp" : settings, GetDataDir(/* net_specific= */ true)); + *filepath = fsbridge::AbsPathJoin(GetDataDir(/* net_specific= */ true), temp ? settings + ".tmp" : settings); } return true; } @@ -1047,27 +1047,36 @@ bool FileCommit(FILE *file) LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); return false; } -#else - #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; - } - #elif defined(MAC_OSX) && defined(F_FULLFSYNC) +#elif defined(MAC_OSX) && defined(F_FULLFSYNC) if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); return false; } - #else +#elif 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; + } +#else if (fsync(fileno(file)) != 0 && errno != EINVAL) { LogPrintf("%s: fsync failed: %d\n", __func__, errno); return false; } - #endif #endif return true; } +void DirectoryCommit(const fs::path &dirname) +{ +#ifndef WIN32 + FILE* file = fsbridge::fopen(dirname, "r"); + if (file) { + fsync(fileno(file)); + fclose(file); + } +#endif +} + bool TruncateFile(FILE *file, unsigned int length) { #if defined(WIN32) return _chsize(_fileno(file), length) == 0; @@ -1302,7 +1311,7 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) if (path.is_absolute()) { return path; } - return fs::absolute(path, GetDataDir(net_specific)); + return fsbridge::AbsPathJoin(GetDataDir(net_specific), path); } void ScheduleBatchPriority() diff --git a/src/util/system.h b/src/util/system.h index 1df194ca84..d06c30bfa7 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -35,8 +35,6 @@ #include <utility> #include <vector> -#include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted - class UniValue; // Application startup time (used for uptime calculation) @@ -56,11 +54,23 @@ bool error(const char* fmt, const Args&... args) } void PrintExceptionContinue(const std::exception *pex, const char* pszThread); + +/** + * Ensure file contents are fully committed to disk, using a platform-specific + * feature analogous to fsync(). + */ bool FileCommit(FILE *file); + +/** + * Sync directory contents. This is required on some environments to ensure that + * newly created files are committed to disk. + */ +void DirectoryCommit(const fs::path &dirname); + bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); -bool RenameOver(fs::path src, fs::path dest); +[[nodiscard]] bool RenameOver(fs::path src, fs::path dest); bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false); void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name); bool DirIsWritable(const fs::path& directory); @@ -188,7 +198,7 @@ protected: std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args); std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args); - NODISCARD bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false); + [[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false); /** * Returns true if settings values from the default section should be used, @@ -220,8 +230,8 @@ public: */ void SelectConfigNetwork(const std::string& network); - NODISCARD bool ParseParameters(int argc, const char* const argv[], std::string& error); - NODISCARD bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); + [[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error); + [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); /** * Log warnings for options in m_section_only_args when @@ -438,11 +448,6 @@ template <typename Callable> void TraceThread(const char* name, Callable func) func(); LogPrintf("%s thread exit\n", name); } - catch (const boost::thread_interrupted&) - { - LogPrintf("%s thread interrupt\n", name); - throw; - } catch (const std::exception& e) { PrintExceptionContinue(&e, name); throw; diff --git a/src/util/time.h b/src/util/time.h index af934e423b..c69f604dc6 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -6,9 +6,11 @@ #ifndef BITCOIN_UTIL_TIME_H #define BITCOIN_UTIL_TIME_H +#include <chrono> #include <stdint.h> #include <string> -#include <chrono> + +using namespace std::chrono_literals; void UninterruptibleSleep(const std::chrono::microseconds& n); diff --git a/src/util/trace.h b/src/util/trace.h new file mode 100644 index 0000000000..9c92cb10e7 --- /dev/null +++ b/src/util/trace.h @@ -0,0 +1,45 @@ +// 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_UTIL_TRACE_H +#define BITCOIN_UTIL_TRACE_H + +#ifdef ENABLE_TRACING + +#include <sys/sdt.h> + +#define TRACE(context, event) DTRACE_PROBE(context, event) +#define TRACE1(context, event, a) DTRACE_PROBE1(context, event, a) +#define TRACE2(context, event, a, b) DTRACE_PROBE2(context, event, a, b) +#define TRACE3(context, event, a, b, c) DTRACE_PROBE3(context, event, a, b, c) +#define TRACE4(context, event, a, b, c, d) DTRACE_PROBE4(context, event, a, b, c, d) +#define TRACE5(context, event, a, b, c, d, e) DTRACE_PROBE5(context, event, a, b, c, d, e) +#define TRACE6(context, event, a, b, c, d, e, f) DTRACE_PROBE6(context, event, a, b, c, d, e, f) +#define TRACE7(context, event, a, b, c, d, e, f, g) DTRACE_PROBE7(context, event, a, b, c, d, e, f, g) +#define TRACE8(context, event, a, b, c, d, e, f, g, h) DTRACE_PROBE8(context, event, a, b, c, d, e, f, g, h) +#define TRACE9(context, event, a, b, c, d, e, f, g, h, i) DTRACE_PROBE9(context, event, a, b, c, d, e, f, g, h, i) +#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j) DTRACE_PROBE10(context, event, a, b, c, d, e, f, g, h, i, j) +#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k) DTRACE_PROBE11(context, event, a, b, c, d, e, f, g, h, i, j, k) +#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l) DTRACE_PROBE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l) + +#else + +#define TRACE(context, event) +#define TRACE1(context, event, a) +#define TRACE2(context, event, a, b) +#define TRACE3(context, event, a, b, c) +#define TRACE4(context, event, a, b, c, d) +#define TRACE5(context, event, a, b, c, d, e) +#define TRACE6(context, event, a, b, c, d, e, f) +#define TRACE7(context, event, a, b, c, d, e, f, g) +#define TRACE8(context, event, a, b, c, d, e, f, g, h) +#define TRACE9(context, event, a, b, c, d, e, f, g, h, i) +#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j) +#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k) +#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l) + +#endif + + +#endif /* BITCOIN_UTIL_TRACE_H */ diff --git a/src/util/translation.h b/src/util/translation.h index 695d6dac96..99899ef3c2 100644 --- a/src/util/translation.h +++ b/src/util/translation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 The Bitcoin Core developers +// 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. diff --git a/src/validation.cpp b/src/validation.cpp index 71402ef263..38df71b994 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -22,7 +22,6 @@ #include <logging/timer.h> #include <node/ui_interface.h> #include <optional.h> -#include <policy/fees.h> #include <policy/policy.h> #include <policy/settings.h> #include <pow.h> @@ -148,8 +147,6 @@ arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); -CBlockPolicyEstimator feeEstimator; - // Internal stuff namespace { CBlockIndex* pindexBestInvalid = nullptr; @@ -170,17 +167,19 @@ namespace { std::set<int> setDirtyFileInfo; } // anon namespace -CBlockIndex* LookupBlockIndex(const uint256& hash) +CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) { AssertLockHeld(cs_main); - BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash); - return it == g_chainman.BlockIndex().end() ? nullptr : it->second; + assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index)); + BlockMap::const_iterator it = m_block_index.find(hash); + return it == m_block_index.end() ? nullptr : it->second; } -CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) +CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { AssertLockHeld(cs_main); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this)); // Find the latest block common to locator and chain - we expect that // locator.vHave is sorted descending by height. for (const uint256& hash : locator.vHave) { @@ -407,36 +406,41 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); } -// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool -// were somehow broken and returning the wrong scriptPubKeys -static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool, - unsigned int flags, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +/** +* Checks to avoid mempool polluting consensus critical paths since cached +* signature and script validity results will be reused if we validate this +* transaction again during block validation. +* */ +static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state, + const CCoinsViewCache& view, const CTxMemPool& pool, + unsigned int flags, PrecomputedTransactionData& txdata) + EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) +{ AssertLockHeld(cs_main); - - // pool.cs should be locked already, but go ahead and re-take the lock here - // to enforce that mempool doesn't change between when we check the view - // and when we actually call through to CheckInputScripts - LOCK(pool.cs); + AssertLockHeld(pool.cs); assert(!tx.IsCoinBase()); for (const CTxIn& txin : tx.vin) { const Coin& coin = view.AccessCoin(txin.prevout); - // AcceptToMemoryPoolWorker has already checked that the coins are - // available, so this shouldn't fail. If the inputs are not available - // here then return false. + // This coin was checked in PreChecks and MemPoolAccept + // has been holding cs_main since then. + Assume(!coin.IsSpent()); if (coin.IsSpent()) return false; - // Check equivalence for available inputs. + // If the Coin is available, there are 2 possibilities: + // it is available in our current ChainstateActive UTXO set, + // or it's a UTXO provided by a transaction in our mempool. + // Ensure the scriptPubKeys in Coins from CoinsView are correct. const CTransactionRef& txFrom = pool.get(txin.prevout.hash); if (txFrom) { assert(txFrom->GetHash() == txin.prevout.hash); assert(txFrom->vout.size() > txin.prevout.n); assert(txFrom->vout[txin.prevout.n] == coin.out); } else { - const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout); - assert(!coinFromDisk.IsSpent()); - assert(coinFromDisk.out == coin.out); + const Coin& coinFromUTXOSet = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout); + assert(!coinFromUTXOSet.IsSpent()); + assert(coinFromUTXOSet.out == coin.out); } } @@ -449,7 +453,7 @@ namespace { class MemPoolAccept { public: - MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool), + explicit MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool), m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)), m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000), m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)), @@ -482,7 +486,7 @@ private: // All the intermediate state that gets passed between the various levels // of checking a given transaction. struct Workspace { - Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {} + explicit Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {} std::set<uint256> m_conflicts; CTxMemPool::setEntries m_all_conflicting; CTxMemPool::setEntries m_ancestors; @@ -505,13 +509,13 @@ private: // Run the script checks using our policy flags. As this can be slow, we should // only invoke this on transactions that have otherwise passed policy checks. - bool PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // Re-run the script checks, using consensus flags, and try to cache the // result in the scriptcache. This should be done after // PolicyScriptChecks(). This requires that all inputs either be in our // utxo set or in the mempool. - bool ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // Try to add the transaction to the mempool, removing any conflicts first. // Returns true if the transaction is in the mempool after any size @@ -519,7 +523,7 @@ private: bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // Compare a package's feerate against minimum allowed. - bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) + bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs) { CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size); if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) { @@ -640,7 +644,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) LockPoints lp; m_view.SetBackend(m_viewmempool); - CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip(); + const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip(); // do all inputs exist? for (const CTxIn& txin : tx.vin) { if (!coins_cache.HaveCoinInCache(txin.prevout)) { @@ -680,7 +684,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final"); CAmount nFees = 0; - if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) { + if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) { return false; // state filled in by CheckTxInputs } @@ -696,7 +700,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs"); } - // Check for non-standard witness in P2WSH + // Check for non-standard witnesses. if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view)) return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard"); @@ -921,7 +925,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) return true; } -bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) +bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata) { const CTransaction& tx = *ws.m_ptx; @@ -948,7 +952,7 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, Precompute return true; } -bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) +bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata) { const CTransaction& tx = *ws.m_ptx; const uint256& hash = ws.m_hash; @@ -1260,8 +1264,8 @@ void CoinsViews::InitCache() } CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash) - : m_blockman(blockman), - m_mempool(mempool), + : m_mempool(mempool), + m_blockman(blockman), m_from_snapshot_blockhash(from_snapshot_blockhash) {} void CChainState::InitCoinsDB( @@ -1408,9 +1412,10 @@ bool CScriptCheck::operator()() { return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error); } -int GetSpendHeight(const CCoinsViewCache& inputs) +int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs) { - LOCK(cs_main); + AssertLockHeld(cs_main); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this)); CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); return pindexPrev->nHeight + 1; } @@ -1761,9 +1766,14 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationSt static CCheckQueue<CScriptCheck> scriptcheckqueue(128); -void ThreadScriptCheck(int worker_num) { - util::ThreadRename(strprintf("scriptch.%i", worker_num)); - scriptcheckqueue.Thread(); +void StartScriptCheckWorkerThreads(int threads_num) +{ + scriptcheckqueue.StartWorkerThreads(threads_num); +} + +void StopScriptCheckWorkerThreads() +{ + scriptcheckqueue.StopWorkerThreads(); } VersionBitsCache versionbitscache GUARDED_BY(cs_main); @@ -2760,7 +2770,7 @@ static SynchronizationState GetSynchronizationState(bool init) return SynchronizationState::INIT_DOWNLOAD; } -static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { +static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) { bool fNotify = false; bool fInitialBlockDownload = false; static CBlockIndex* pindexHeaderOld = nullptr; @@ -2771,7 +2781,8 @@ static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { if (pindexHeader != pindexHeaderOld) { fNotify = true; - fInitialBlockDownload = ::ChainstateActive().IsInitialBlockDownload(); + assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + fInitialBlockDownload = chainstate.IsInitialBlockDownload(); pindexHeaderOld = pindexHeader; } } @@ -2888,10 +2899,6 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar return true; } -bool ActivateBestChain(BlockValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { - return ::ChainstateActive().ActivateBestChain(state, chainparams, std::move(pblock)); -} - bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) { { @@ -3391,14 +3398,14 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc return commitment; } -//! Returns last CBlockIndex* that is a checkpoint -static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) { const uint256& hash = i.second; + assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this)); CBlockIndex* pindex = LookupBlockIndex(hash); if (pindex) { return pindex; @@ -3416,7 +3423,7 @@ static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOC * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ -static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; @@ -3431,7 +3438,8 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio // Don't accept any forks from the main chain prior to last checkpoint. // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our // BlockIndex(). - CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints()); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); + CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints()); if (pcheckpoint && nHeight < pcheckpoint->nHeight) { LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight); return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "bad-fork-prior-to-checkpoint"); @@ -3551,11 +3559,10 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS // Check for duplicate uint256 hash = block.GetHash(); BlockMap::iterator miSelf = m_block_index.find(hash); - CBlockIndex *pindex = nullptr; if (hash != chainparams.GetConsensus().hashGenesisBlock) { if (miSelf != m_block_index.end()) { // Block header is already known. - pindex = miSelf->second; + CBlockIndex* pindex = miSelf->second; if (ppindex) *ppindex = pindex; if (pindex->nStatus & BLOCK_FAILED_MASK) { @@ -3582,7 +3589,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS LogPrintf("ERROR: %s: prev block invalid\n", __func__); return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); } - if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) + if (!ContextualCheckBlockHeader(block, state, *this, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString()); /* Determine if this block descends from any block which has been found @@ -3624,8 +3631,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS } } } - if (pindex == nullptr) - pindex = AddToBlockIndex(block); + CBlockIndex* pindex = AddToBlockIndex(block); if (ppindex) *ppindex = pindex; @@ -3636,6 +3642,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS // Exposed wrapper for AcceptBlockHeader bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) { + assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate())); AssertLockNotHeld(cs_main); { LOCK(cs_main); @@ -3643,7 +3650,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast bool accepted = m_blockman.AcceptBlockHeader( header, state, chainparams, &pindex); - ::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus()); + ActiveChainstate().CheckBlockIndex(chainparams.GetConsensus()); if (!accepted) { return false; @@ -3653,8 +3660,8 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& } } } - if (NotifyHeaderTip()) { - if (::ChainstateActive().IsInitialBlockDownload() && ppindex && *ppindex) { + if (NotifyHeaderTip(ActiveChainstate())) { + if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) { LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight); } } @@ -3766,6 +3773,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) { AssertLockNotHeld(cs_main); + assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate())); { CBlockIndex *pindex = nullptr; @@ -3781,7 +3789,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus()); if (ret) { // Store to disk - ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); + ret = ActiveChainstate().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); @@ -3789,20 +3797,27 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s } } - NotifyHeaderTip(); + NotifyHeaderTip(ActiveChainstate()); BlockValidationState state; // Only used to report errors, not invalidity - ignore it - if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock)) + if (!ActiveChainstate().ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString()); return true; } -bool TestBlockValidity(BlockValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) +bool TestBlockValidity(BlockValidationState& state, + const CChainParams& chainparams, + CChainState& chainstate, + const CBlock& block, + CBlockIndex* pindexPrev, + bool fCheckPOW, + bool fCheckMerkleRoot) { AssertLockHeld(cs_main); - assert(pindexPrev && pindexPrev == ::ChainActive().Tip()); - CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip()); + assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip()); + CCoinsViewCache viewNew(&chainstate.CoinsTip()); uint256 block_hash(block.GetHash()); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; @@ -3810,13 +3825,14 @@ bool TestBlockValidity(BlockValidationState& state, const CChainParams& chainpar indexDummy.phashBlock = &block_hash; // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) + assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman)); + if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString()); if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString()); if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString()); - if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) + if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) return false; assert(state.IsValid()); @@ -4162,7 +4178,7 @@ bool CChainState::LoadChainTip(const CChainParams& chainparams) } // Load pointer to end of best chain - CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock()); + CBlockIndex* pindex = m_blockman.LookupBlockIndex(coins_cache.GetBestBlock()); if (!pindex) { return false; } @@ -4592,7 +4608,7 @@ bool LoadGenesisBlock(const CChainParams& chainparams) return ::ChainstateActive().LoadGenesisBlock(chainparams); } -void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp) +void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp) { // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent; @@ -4641,7 +4657,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi { LOCK(cs_main); // detect out of order blocks, and store them for later - if (hash != chainparams.GetConsensus().hashGenesisBlock && !LookupBlockIndex(block.hashPrevBlock)) { + assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman)); + if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) { LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), block.hashPrevBlock.ToString()); if (dbp) @@ -4650,10 +4667,12 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi } // process in case the block isn't known yet - CBlockIndex* pindex = LookupBlockIndex(hash); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman)); + CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash); if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) { BlockValidationState state; - if (::ChainstateActive().AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { nLoaded++; } if (state.IsError()) { @@ -4667,12 +4686,14 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi // Activate the genesis block so normal node progress can continue if (hash == chainparams.GetConsensus().hashGenesisBlock) { BlockValidationState state; + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); if (!ActivateBestChain(state, chainparams, nullptr)) { break; } } - NotifyHeaderTip(); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + NotifyHeaderTip(*this); // Recursively process earlier encountered successors of this block std::deque<uint256> queue; @@ -4690,7 +4711,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi head.ToString()); LOCK(cs_main); BlockValidationState dummy; - if (::ChainstateActive().AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) { nLoaded++; queue.push_back(pblockrecursive->GetHash()); @@ -4698,7 +4720,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi } range.first++; mapBlocksUnknownParent.erase(it); - NotifyHeaderTip(); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + NotifyHeaderTip(*this); } } } catch (const std::exception& e) { @@ -5038,15 +5061,9 @@ bool LoadMempool(CTxMemPool& pool) pool.PrioritiseTransaction(i.first, i.second); } - // TODO: remove this try except in v0.22 std::set<uint256> unbroadcast_txids; - try { - file >> unbroadcast_txids; - unbroadcast = unbroadcast_txids.size(); - } catch (const std::exception&) { - // mempool.dat files created prior to v0.21 will not have an - // unbroadcast set. No need to log a failure if parsing fails here. - } + file >> unbroadcast_txids; + unbroadcast = unbroadcast_txids.size(); for (const auto& txid : unbroadcast_txids) { // Ensure transactions were accepted to mempool then add to // unbroadcast set. @@ -5110,7 +5127,9 @@ bool DumpMempool(const CTxMemPool& pool) if (!FileCommit(file.Get())) throw std::runtime_error("FileCommit failed"); file.fclose(); - RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); + if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) { + throw std::runtime_error("Rename failed"); + } int64_t last = GetTimeMicros(); LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO); } catch (const std::exception& e) { @@ -5140,6 +5159,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin } Optional<uint256> ChainstateManager::SnapshotBlockhash() const { + LOCK(::cs_main); // for m_active_chainstate access if (m_active_chainstate != nullptr) { // If a snapshot chainstate exists, it will always be our active. return m_active_chainstate->m_from_snapshot_blockhash; @@ -5186,13 +5206,14 @@ CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const CChainState& ChainstateManager::ActiveChainstate() const { + LOCK(::cs_main); assert(m_active_chainstate); return *m_active_chainstate; } bool ChainstateManager::IsSnapshotActive() const { - return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get(); + return m_snapshot_chainstate && WITH_LOCK(::cs_main, return m_active_chainstate) == m_snapshot_chainstate.get(); } CChainState& ChainstateManager::ValidatedChainstate() const @@ -5223,7 +5244,10 @@ void ChainstateManager::Reset() { m_ibd_chainstate.reset(); m_snapshot_chainstate.reset(); - m_active_chainstate = nullptr; + { + LOCK(::cs_main); + m_active_chainstate = nullptr; + } m_snapshot_validated = false; } diff --git a/src/validation.h b/src/validation.h index ffb038ad75..e86b3d3af8 100644 --- a/src/validation.h +++ b/src/validation.h @@ -23,6 +23,7 @@ #include <txdb.h> #include <versionbits.h> #include <serialize.h> +#include <util/hasher.h> #include <atomic> #include <map> @@ -39,10 +40,10 @@ class CBlockIndex; class CBlockTreeDB; class CBlockUndo; class CChainParams; +class CCheckpointData; class CInv; class CConnman; class CScriptCheck; -class CBlockPolicyEstimator; class CTxMemPool; class ChainstateManager; class TxValidationState; @@ -94,14 +95,6 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3; // 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; -struct BlockHasher -{ - // this used to call `GetCheapHash()` in uint256, which was later moved; the - // cheap hash function simply calls ReadLE64() however, so the end result is - // identical - size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } -}; - /** Current sync state passed to tip changed callbacks. */ enum class SynchronizationState { INIT_REINDEX, @@ -110,7 +103,6 @@ enum class SynchronizationState { }; extern RecursiveMutex cs_main; -extern CBlockPolicyEstimator feeEstimator; typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap; extern Mutex g_best_block_mutex; extern std::condition_variable g_best_block_cv; @@ -152,14 +144,14 @@ extern const std::vector<std::string> CHECKLEVEL_DOC; FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false); /** Translation to a filesystem path */ fs::path GetBlockPosFilename(const FlatFilePos &pos); -/** Import blocks from an external file */ -void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr); /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ bool LoadGenesisBlock(const CChainParams& chainparams); /** Unload database information */ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman); -/** Run an instance of the script checking thread */ -void ThreadScriptCheck(int worker_num); +/** Run instances of script checking worker threads */ +void StartScriptCheckWorkerThreads(int threads_num); +/** Stop all of the script checking worker threads */ +void StopScriptCheckWorkerThreads(); /** * Return transaction from the block at block_index. * If block_index is not provided, fall back to mempool. @@ -173,13 +165,6 @@ void ThreadScriptCheck(int worker_num); * @returns The tx if found, otherwise nullptr */ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock); -/** - * Find the best known block, and make it the tip of the block chain - * - * May not be called with cs_main held. May not be called in a - * validationinterface callback. - */ -bool ActivateBestChain(BlockValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ @@ -298,7 +283,13 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex); bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Check a block is completely valid from start to finish (only works on top of our current best block) */ -bool TestBlockValidity(BlockValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool TestBlockValidity(BlockValidationState& state, + const CChainParams& chainparams, + CChainState& chainstate, + const CBlock& block, + CBlockIndex* pindexPrev, + bool fCheckPOW = true, + bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Check whether witness commitments are required for a block, and whether to enforce NULLDUMMY (BIP 147) rules. * Note that transaction witness validation rules are always enforced when P2SH is enforced. */ @@ -318,11 +309,6 @@ public: bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); }; -CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - -/** Find the last common block between the parameter chain and a locator. */ -CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - enum DisconnectResult { DISCONNECT_OK, // All good. @@ -440,6 +426,21 @@ public: const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + /** Find the last common block between the parameter chain and a locator. */ + CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + //! Returns last CBlockIndex* that is a checkpoint + CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + /** + * Return the spend height, which is one more than the inputs.GetBestBlock(). + * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) + * This is also true for mempool checks. + */ + int GetSpendHeight(const CCoinsViewCache& inputs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + ~BlockManager() { Unload(); } @@ -532,11 +533,6 @@ protected: */ mutable std::atomic<bool> m_cached_finished_ibd{false}; - //! Reference to a BlockManager instance which itself is shared across all - //! CChainState instances. Keeping a local reference allows us to test more - //! easily as opposed to referencing a global. - BlockManager& m_blockman; - //! mempool that is kept in sync with the chain CTxMemPool& m_mempool; @@ -544,6 +540,10 @@ protected: std::unique_ptr<CoinsViews> m_coins_views; public: + //! Reference to a BlockManager instance which itself is shared across all + //! CChainState instances. + BlockManager& m_blockman; + explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash = uint256()); /** @@ -564,7 +564,7 @@ public: //! @returns whether or not the CoinsViews object has been fully initialized and we can //! safely flush this object to disk. - bool CanFlushToDisk() EXCLUSIVE_LOCKS_REQUIRED(cs_main) { + bool CanFlushToDisk() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return m_coins_views && m_coins_views->m_cacheview; } @@ -620,6 +620,9 @@ public: bool ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + /** Import blocks from an external file */ + void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr); + /** * Update the on-disk chain state. * The caches and indexes are flushed depending on the mode we're called with @@ -645,9 +648,10 @@ public: void PruneAndFlush(); /** - * Make the best chain active, in multiple steps. The result is either failure - * or an activated best chain. pblock is either nullptr or a pointer to a block - * that is already loaded (to avoid loading it again from disk). + * Find the best known block, and make it the tip of the block chain. The + * result is either failure or an activated best chain. pblock is either + * nullptr or a pointer to a block that is already loaded (to avoid loading + * it again from disk). * * ActivateBestChain is split into steps (see ActivateBestChainStep) so that * we avoid holding cs_main for an extended period of time; the length of this @@ -661,7 +665,7 @@ public: bool ActivateBestChain( BlockValidationState& state, const CChainParams& chainparams, - std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main); + std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(cs_main); bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -824,7 +828,7 @@ private: //! This is especially important when, e.g., calling ActivateBestChain() //! on all chainstates because we are not able to hold ::cs_main going into //! that call. - CChainState* m_active_chainstate{nullptr}; + CChainState* m_active_chainstate GUARDED_BY(::cs_main) {nullptr}; //! If true, the assumed-valid chainstate has been fully validated //! by the background validation chainstate. @@ -952,13 +956,6 @@ CChain& ChainActive(); /** Global variable that points to the active block tree (protected by cs_main) */ extern std::unique_ptr<CBlockTreeDB> pblocktree; -/** - * Return the spend height, which is one more than the inputs.GetBestBlock(). - * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) - * This is also true for mempool checks. - */ -int GetSpendHeight(const CCoinsViewCache& inputs); - extern VersionBitsCache versionbitscache; /** diff --git a/src/version.h b/src/version.h index 019c3a3ae7..ee646eefc3 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2018 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp index 20dfc044ca..fa41bad46d 100644 --- a/src/versionbitsinfo.cpp +++ b/src/versionbitsinfo.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 8c43e072a3..ad40e6da9a 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -53,16 +53,13 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const } /** - * @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. + * @param[in] env_directory Path to environment directory * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment * erases the weak pointer from the g_dbenvs map. * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map. */ -std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename) +std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory) { - fs::path env_directory; - SplitWalletPath(wallet_path, env_directory, database_filename); LOCK(cs_db); auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>()); if (inserted.second) { @@ -726,6 +723,23 @@ bool BerkeleyBatch::TxnAbort() return (ret == 0); } +bool BerkeleyDatabaseSanityCheck() +{ + int major, minor; + DbEnv::version(&major, &minor, nullptr); + + /* If the major version differs, or the minor version of library is *older* + * than the header that was compiled against, flag an error. + */ + if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) { + LogPrintf("BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n", + DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor); + return false; + } + + return true; +} + std::string BerkeleyDatabaseVersion() { return DbEnv::version(nullptr, nullptr, nullptr); @@ -808,21 +822,14 @@ 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) { + fs::path data_file = BDBDataFile(path); 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); + std::string data_filename = data_file.filename().string(); + std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path()); 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; @@ -839,28 +846,3 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con 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 9073c1b6b3..a8209587d7 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -56,7 +56,7 @@ public: std::unordered_map<std::string, WalletDatabaseFileId> m_fileids; std::condition_variable_any m_db_in_use; - BerkeleyEnvironment(const fs::path& env_directory); + explicit BerkeleyEnvironment(const fs::path& env_directory); BerkeleyEnvironment(); ~BerkeleyEnvironment(); void Reset(); @@ -83,11 +83,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); - -/** Check format of database file */ -bool IsBDBFile(const fs::path& path); +/** Get BerkeleyEnvironment given a directory path. */ +std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory); class BerkeleyBatch; @@ -226,8 +223,9 @@ public: std::string BerkeleyDatabaseVersion(); -//! Check if Berkeley database exists at specified path. -bool ExistsBerkeleyDatabase(const fs::path& path); +/** Perform sanity check of runtime BDB version versus linked BDB version. + */ +bool BerkeleyDatabaseSanityCheck(); //! 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); diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 1a45a2b313..10f89e3a6f 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -300,8 +300,26 @@ 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) { +void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only) { + // Compute the effective value first + const CAmount coin_fee = output.m_input_bytes < 0 ? 0 : m_effective_feerate.GetFee(output.m_input_bytes); + const CAmount ev = output.txout.nValue - coin_fee; + + // Filter for positive only here before adding the coin + if (positive_only && ev <= 0) return; + m_outputs.push_back(output); + CInputCoin& coin = m_outputs.back(); + + coin.m_fee = coin_fee; + fee += coin.m_fee; + + coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : m_long_term_feerate.GetFee(coin.m_input_bytes); + long_term_fee += coin.m_long_term_fee; + + coin.effective_value = ev; + effective_value += coin.effective_value; + m_from_me &= from_me; m_value += output.txout.nValue; m_depth = std::min(m_depth, depth); @@ -312,20 +330,6 @@ 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 += 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.txout.nValue; - effective_value -= output.effective_value; - fee -= output.m_fee; - long_term_fee -= output.m_long_term_fee; - return m_outputs.erase(it); } bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const @@ -334,35 +338,3 @@ 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 49c1134ec6..f0e1addaf1 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018 The Bitcoin Core developers +// 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. @@ -6,11 +6,10 @@ #define BITCOIN_WALLET_COINSELECTION_H #include <amount.h> +#include <policy/feerate.h> #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 @@ -63,9 +62,11 @@ struct CoinEligibilityFilter const int conf_theirs; const uint64_t max_ancestors; const uint64_t max_descendants; + const bool m_include_partial_groups{false}; //! Include partial destination groups when avoid_reuse and there are full groups CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {} CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {} + CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {} }; struct OutputGroup @@ -78,27 +79,18 @@ struct OutputGroup size_t m_descendants{0}; CAmount effective_value{0}; CAmount fee{0}; + CFeeRate m_effective_feerate{0}; CAmount long_term_fee{0}; + CFeeRate m_long_term_feerate{0}; OutputGroup() {} - OutputGroup(std::vector<CInputCoin>&& outputs, bool from_me, CAmount value, int depth, size_t ancestors, size_t descendants) - : m_outputs(std::move(outputs)) - , m_from_me(from_me) - , m_value(value) - , m_depth(depth) - , m_ancestors(ancestors) - , m_descendants(descendants) + OutputGroup(const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate) : + m_effective_feerate(effective_feerate), + m_long_term_feerate(long_term_feerate) {} - OutputGroup(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) : OutputGroup() { - Insert(output, depth, from_me, ancestors, descendants); - } - 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(); + void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only); + bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const; }; 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/crypter.h b/src/wallet/crypter.h index f2df786e2e..f7325541a9 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index bd1d114730..cd49baeb78 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -3,23 +3,130 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <chainparams.h> #include <fs.h> +#include <logging.h> #include <wallet/db.h> #include <string> -void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename) +std::vector<fs::path> ListDatabases(const fs::path& wallet_dir) +{ + const size_t offset = wallet_dir.string().size() + 1; + std::vector<fs::path> paths; + boost::system::error_code ec; + + for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) { + if (ec) { + LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string()); + continue; + } + + try { + // Get wallet path relative to walletdir by removing walletdir from the wallet path. + // 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 && + (IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(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 && IsBDBFile(it->path())) { + if (it->path().filename() == "wallet.dat") { + // Found top-level wallet.dat btree file, add top level directory "" + // as a wallet. + paths.emplace_back(); + } else { + // Found top-level btree file not called wallet.dat. Current bitcoin + // software will never create these files but will allow them to be + // opened in a shared database environment for backwards compatibility. + // Add it to the list of available wallets. + paths.emplace_back(path); + } + } + } catch (const std::exception& e) { + LogPrintf("%s: Error scanning %s: %s\n", __func__, it->path().string(), e.what()); + it.no_push(); + } + } + + return paths; +} + +fs::path BDBDataFile(const fs::path& wallet_path) { if (fs::is_regular_file(wallet_path)) { // Special case for backwards compatibility: if wallet path points to an // existing file, treat it as the path to a BDB data file in a parent // directory that also contains BDB log files. - env_directory = wallet_path.parent_path(); - database_filename = wallet_path.filename().string(); + return wallet_path; } else { // Normal case: Interpret wallet path as a directory path containing // data and log files. - env_directory = wallet_path; - database_filename = "wallet.dat"; + return wallet_path / "wallet.dat"; } } + +fs::path SQLiteDataFile(const fs::path& path) +{ + return path / "wallet.dat"; +} + +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; +} + +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/db.h b/src/wallet/db.h index 940d1cd242..2c75486a44 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -223,6 +223,14 @@ enum class DatabaseStatus { FAILED_ENCRYPT, }; +/** Recursively list database paths in directory. */ +std::vector<fs::path> ListDatabases(const fs::path& path); + std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); +fs::path BDBDataFile(const fs::path& path); +fs::path SQLiteDataFile(const fs::path& path); +bool IsBDBFile(const fs::path& path); +bool IsSQLiteFile(const fs::path& path); + #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp new file mode 100644 index 0000000000..e314107988 --- /dev/null +++ b/src/wallet/dump.cpp @@ -0,0 +1,282 @@ +// 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/dump.h> + +#include <util/translation.h> +#include <wallet/wallet.h> + +static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP"; +uint32_t DUMP_VERSION = 1; + +bool DumpWallet(CWallet& wallet, bilingual_str& error) +{ + // Get the dumpfile + std::string dump_filename = gArgs.GetArg("-dumpfile", ""); + if (dump_filename.empty()) { + error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided."); + return false; + } + + fs::path path = dump_filename; + path = fs::absolute(path); + if (fs::exists(path)) { + error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), path.string()); + return false; + } + fsbridge::ofstream dump_file; + dump_file.open(path); + if (dump_file.fail()) { + error = strprintf(_("Unable to open %s for writing"), path.string()); + return false; + } + + CHashWriter hasher(0, 0); + + WalletDatabase& db = wallet.GetDatabase(); + std::unique_ptr<DatabaseBatch> batch = db.MakeBatch(); + + bool ret = true; + if (!batch->StartCursor()) { + error = _("Error: Couldn't create cursor into database"); + ret = false; + } + + // Write out a magic string with version + std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION); + dump_file.write(line.data(), line.size()); + hasher.write(line.data(), line.size()); + + // Write out the file format + line = strprintf("%s,%s\n", "format", db.Format()); + dump_file.write(line.data(), line.size()); + hasher.write(line.data(), line.size()); + + if (ret) { + + // Read the records + while (true) { + CDataStream ss_key(SER_DISK, CLIENT_VERSION); + CDataStream ss_value(SER_DISK, CLIENT_VERSION); + bool complete; + ret = batch->ReadAtCursor(ss_key, ss_value, complete); + if (complete) { + ret = true; + break; + } else if (!ret) { + error = _("Error reading next record from wallet database"); + break; + } + std::string key_str = HexStr(ss_key); + std::string value_str = HexStr(ss_value); + line = strprintf("%s,%s\n", key_str, value_str); + dump_file.write(line.data(), line.size()); + hasher.write(line.data(), line.size()); + } + } + + batch->CloseCursor(); + batch.reset(); + + // Close the wallet after we're done with it. The caller won't be doing this + wallet.Close(); + + if (ret) { + // Write the hash + tfm::format(dump_file, "checksum,%s\n", HexStr(hasher.GetHash())); + dump_file.close(); + } else { + // Remove the dumpfile on failure + dump_file.close(); + fs::remove(path); + } + + return ret; +} + +// The standard wallet deleter function blocks on the validation interface +// queue, which doesn't exist for the bitcoin-wallet. Define our own +// deleter here. +static void WalletToolReleaseWallet(CWallet* wallet) +{ + wallet->WalletLogPrintf("Releasing wallet\n"); + wallet->Close(); + delete wallet; +} + +bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings) +{ + // Get the dumpfile + std::string dump_filename = gArgs.GetArg("-dumpfile", ""); + if (dump_filename.empty()) { + error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided."); + return false; + } + + fs::path dump_path = dump_filename; + dump_path = fs::absolute(dump_path); + if (!fs::exists(dump_path)) { + error = strprintf(_("Dump file %s does not exist."), dump_path.string()); + return false; + } + fsbridge::ifstream dump_file(dump_path); + + // Compute the checksum + CHashWriter hasher(0, 0); + uint256 checksum; + + // Check the magic and version + std::string magic_key; + std::getline(dump_file, magic_key, ','); + std::string version_value; + std::getline(dump_file, version_value, '\n'); + if (magic_key != DUMP_MAGIC) { + error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC); + dump_file.close(); + return false; + } + // Check the version number (value of first record) + uint32_t ver; + if (!ParseUInt32(version_value, &ver)) { + error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value); + dump_file.close(); + return false; + } + if (ver != DUMP_VERSION) { + error = strprintf(_("Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value); + dump_file.close(); + return false; + } + std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value); + hasher.write(magic_hasher_line.data(), magic_hasher_line.size()); + + // Get the stored file format + std::string format_key; + std::getline(dump_file, format_key, ','); + std::string format_value; + std::getline(dump_file, format_value, '\n'); + if (format_key != "format") { + error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key); + dump_file.close(); + return false; + } + // Get the data file format with format_value as the default + std::string file_format = gArgs.GetArg("-format", format_value); + if (file_format.empty()) { + error = _("No wallet file format provided. To use createfromdump, -format=<format> must be provided."); + return false; + } + DatabaseFormat data_format; + if (file_format == "bdb") { + data_format = DatabaseFormat::BERKELEY; + } else if (file_format == "sqlite") { + data_format = DatabaseFormat::SQLITE; + } else { + error = strprintf(_("Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or \"sqlite\"."), file_format); + return false; + } + if (file_format != format_value) { + warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format)); + } + std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value); + hasher.write(format_hasher_line.data(), format_hasher_line.size()); + + DatabaseOptions options; + DatabaseStatus status; + options.require_create = true; + options.require_format = data_format; + std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error); + if (!database) return false; + + // dummy chain interface + bool ret = true; + std::shared_ptr<CWallet> wallet(new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet); + { + LOCK(wallet->cs_wallet); + bool first_run = true; + DBErrors load_wallet_ret = wallet->LoadWallet(first_run); + if (load_wallet_ret != DBErrors::LOAD_OK) { + error = strprintf(_("Error creating %s"), name); + return false; + } + + // Get the database handle + WalletDatabase& db = wallet->GetDatabase(); + std::unique_ptr<DatabaseBatch> batch = db.MakeBatch(); + batch->TxnBegin(); + + // Read the records from the dump file and write them to the database + while (dump_file.good()) { + std::string key; + std::getline(dump_file, key, ','); + std::string value; + std::getline(dump_file, value, '\n'); + + if (key == "checksum") { + std::vector<unsigned char> parsed_checksum = ParseHex(value); + std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin()); + break; + } + + std::string line = strprintf("%s,%s\n", key, value); + hasher.write(line.data(), line.size()); + + if (key.empty() || value.empty()) { + continue; + } + + if (!IsHex(key)) { + error = strprintf(_("Error: Got key that was not hex: %s"), key); + ret = false; + break; + } + if (!IsHex(value)) { + error = strprintf(_("Error: Got value that was not hex: %s"), value); + ret = false; + break; + } + + std::vector<unsigned char> k = ParseHex(key); + std::vector<unsigned char> v = ParseHex(value); + + CDataStream ss_key(k, SER_DISK, CLIENT_VERSION); + CDataStream ss_value(v, SER_DISK, CLIENT_VERSION); + + if (!batch->Write(ss_key, ss_value)) { + error = strprintf(_("Error: Unable to write record to new wallet")); + ret = false; + break; + } + } + + if (ret) { + uint256 comp_checksum = hasher.GetHash(); + if (checksum.IsNull()) { + error = _("Error: Missing checksum"); + ret = false; + } else if (checksum != comp_checksum) { + error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum)); + ret = false; + } + } + + if (ret) { + batch->TxnCommit(); + } else { + batch->TxnAbort(); + } + + batch.reset(); + + dump_file.close(); + } + wallet.reset(); // The pointer deleter will close the wallet for us. + + // Remove the wallet dir if we have a failure + if (!ret) { + fs::remove_all(wallet_path); + } + + return ret; +} diff --git a/src/wallet/dump.h b/src/wallet/dump.h new file mode 100644 index 0000000000..d0a4f5ef1d --- /dev/null +++ b/src/wallet/dump.h @@ -0,0 +1,17 @@ +// 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_DUMP_H +#define BITCOIN_WALLET_DUMP_H + +#include <fs.h> + +class CWallet; + +struct bilingual_str; + +bool DumpWallet(CWallet& wallet, bilingual_str& error); +bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings); + +#endif // BITCOIN_WALLET_DUMP_H diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 6cbad14de8..5e319d4f95 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -111,7 +111,7 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt return feebumper::Result::OK; } -static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, CCoinControl& coin_control) +static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, const CCoinControl& coin_control) { // Get the fee rate of the original transaction. This is calculated from // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the @@ -215,7 +215,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo // We cannot source new unconfirmed inputs(bip125 rule 2) new_coin_control.m_min_depth = 1; - CTransactionRef tx_new = MakeTransactionRef(); + CTransactionRef tx_new; CAmount fee_ret; int change_pos_in_out = -1; // No requested location for change bilingual_str fail_reason; @@ -231,7 +231,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo // Write back transaction mtx = CMutableTransaction(*tx_new); // Mark new tx not replaceable, if requested. - if (!coin_control.m_signal_bip125_rbf.get_value_or(wallet.m_signal_rbf)) { + if (!coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf)) { for (auto& input : mtx.vin) { if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe; } @@ -256,7 +256,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti errors.push_back(Untranslated("Invalid or non-wallet transaction id")); return Result::MISC_ERROR; } - CWalletTx& oldWtx = it->second; + const CWalletTx& oldWtx = it->second; // make sure the transaction still has no descendants and hasn't been mined in the meantime Result result = PreconditionChecks(wallet, oldWtx, errors); diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 249bc833c6..429101e774 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -49,7 +49,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr // We will use smart fee estimation unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : wallet.m_confirm_target; // By default estimates are economical iff we are signaling opt-in-RBF - bool conservative_estimate = !coin_control.m_signal_bip125_rbf.get_value_or(wallet.m_signal_rbf); + bool conservative_estimate = !coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf); // Allow to override the default fee estimate mode over the CoinControl instance if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 085dde1026..0d2be64dfb 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -86,6 +86,11 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const bool WalletInit::ParameterInteraction() const { +#ifdef USE_BDB + if (!BerkeleyDatabaseSanityCheck()) { + return InitError(Untranslated("A version conflict was detected between the run-time BerkeleyDB library and the one used during compilation.")); + } +#endif if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { for (const std::string& wallet : gArgs.GetArgs("-wallet")) { LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet); diff --git a/src/interfaces/wallet.cpp b/src/wallet/interfaces.cpp index f68016b557..e4e8c50f4f 100644 --- a/src/interfaces/wallet.cpp +++ b/src/wallet/interfaces.cpp @@ -31,9 +31,22 @@ #include <utility> #include <vector> -namespace interfaces { -namespace { +using interfaces::Chain; +using interfaces::FoundBlock; +using interfaces::Handler; +using interfaces::MakeHandler; +using interfaces::Wallet; +using interfaces::WalletAddress; +using interfaces::WalletBalances; +using interfaces::WalletClient; +using interfaces::WalletOrderForm; +using interfaces::WalletTx; +using interfaces::WalletTxOut; +using interfaces::WalletTxStatus; +using interfaces::WalletValueMap; +namespace wallet { +namespace { //! Construct wallet tx struct. WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) { @@ -64,7 +77,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) } //! Construct wallet tx status struct. -WalletTxStatus MakeWalletTxStatus(CWallet& wallet, const CWalletTx& wtx) +WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx) { WalletTxStatus result; result.block_height = wtx.m_confirm.block_height > 0 ? wtx.m_confirm.block_height : std::numeric_limits<int>::max(); @@ -81,7 +94,7 @@ WalletTxStatus MakeWalletTxStatus(CWallet& wallet, const CWalletTx& wtx) } //! Construct wallet TxOut struct. -WalletTxOut MakeWalletTxOut(CWallet& wallet, +WalletTxOut MakeWalletTxOut(const CWallet& wallet, const CWalletTx& wtx, int n, int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) @@ -538,7 +551,7 @@ public: std::vector<std::string> listWalletDir() override { std::vector<std::string> paths; - for (auto& path : ListWalletDir()) { + for (auto& path : ListDatabases(GetWalletDir())) { paths.push_back(path.string()); } return paths; @@ -561,14 +574,14 @@ public: std::vector<std::unique_ptr<Handler>> m_rpc_handlers; std::list<CRPCCommand> m_rpc_commands; }; - } // namespace +} // namespace wallet -std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; } +namespace interfaces { +std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<wallet::WalletImpl>(wallet) : nullptr; } std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args) { - return MakeUnique<WalletClientImpl>(chain, args); + return MakeUnique<wallet::WalletClientImpl>(chain, args); } - } // namespace interfaces diff --git a/src/wallet/ismine.h b/src/wallet/ismine.h index 5cdd7dff80..38ed7e7770 100644 --- a/src/wallet/ismine.h +++ b/src/wallet/ismine.h @@ -14,7 +14,27 @@ class CWallet; class CScript; -/** IsMine() return codes */ +/** + * IsMine() return codes, which depend on ScriptPubKeyMan implementation. + * Not every ScriptPubKeyMan covers all types, please refer to + * https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.21.0.md#ismine-semantics + * for better understanding. + * + * For LegacyScriptPubKeyMan, + * ISMINE_NO: the scriptPubKey is not in the wallet; + * ISMINE_WATCH_ONLY: the scriptPubKey has been imported into the wallet; + * ISMINE_SPENDABLE: the scriptPubKey corresponds to an address owned by the wallet user (can spend with the private key); + * ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user; + * ISMINE_ALL: all ISMINE flags except for USED; + * ISMINE_ALL_USED: all ISMINE flags including USED; + * ISMINE_ENUM_ELEMENTS: the number of isminetype enum elements. + * + * For DescriptorScriptPubKeyMan and future ScriptPubKeyMan, + * ISMINE_NO: the scriptPubKey is not in the wallet; + * ISMINE_SPENDABLE: the scriptPubKey matches a scriptPubKey in the wallet. + * ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user. + * + */ enum isminetype : unsigned int { ISMINE_NO = 0, diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 036fd4956f..30832f983b 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -62,7 +62,7 @@ bool VerifyWallets(interfaces::Chain& chain) std::set<fs::path> wallet_paths; for (const auto& wallet_file : gArgs.GetArgs("-wallet")) { - const fs::path path = fs::absolute(wallet_file, GetWalletDir()); + const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_file); if (!wallet_paths.insert(path).second) { chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file)); diff --git a/src/wallet/load.h b/src/wallet/load.h index e12343de27..7910f0d6e1 100644 --- a/src/wallet/load.h +++ b/src/wallet/load.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 6b46868d10..8505ddc309 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1740,3 +1740,72 @@ RPCHelpMan importdescriptors() }, }; } + +RPCHelpMan listdescriptors() +{ + return RPCHelpMan{ + "listdescriptors", + "\nList descriptors imported into a descriptor-enabled wallet.", + {}, + RPCResult{ + RPCResult::Type::ARR, "", "Response is an array of descriptor objects", + { + {RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::STR, "desc", "Descriptor string representation"}, + {RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"}, + {RPCResult::Type::BOOL, "active", "Activeness flag"}, + {RPCResult::Type::BOOL, "internal", true, "Whether this is internal or external descriptor; defined only for active descriptors"}, + {RPCResult::Type::ARR_FIXED, "range", true, "Defined only for ranged descriptors", { + {RPCResult::Type::NUM, "", "Range start inclusive"}, + {RPCResult::Type::NUM, "", "Range end inclusive"}, + }}, + {RPCResult::Type::NUM, "next", true, "The next index to generate addresses from; defined only for ranged descriptors"}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + if (!wallet) return NullUniValue; + + if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets"); + } + + LOCK(wallet->cs_wallet); + + UniValue response(UniValue::VARR); + const auto active_spk_mans = wallet->GetActiveScriptPubKeyMans(); + for (const auto& spk_man : wallet->GetAllScriptPubKeyMans()) { + const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man); + if (!desc_spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, "Unexpected ScriptPubKey manager type."); + } + UniValue spk(UniValue::VOBJ); + LOCK(desc_spk_man->cs_desc_man); + const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor(); + spk.pushKV("desc", wallet_descriptor.descriptor->ToString()); + spk.pushKV("timestamp", wallet_descriptor.creation_time); + const bool active = active_spk_mans.count(desc_spk_man) != 0; + spk.pushKV("active", active); + const auto& type = wallet_descriptor.descriptor->GetOutputType(); + if (active && type != nullopt) { + spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man); + } + if (wallet_descriptor.descriptor->IsRange()) { + UniValue range(UniValue::VARR); + range.push_back(wallet_descriptor.range_start); + range.push_back(wallet_descriptor.range_end - 1); + spk.pushKV("range", range); + spk.pushKV("next", wallet_descriptor.next_index); + } + response.push_back(spk); + } + + return response; +}, + }; +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9425efa2dd..46de273d63 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -197,25 +197,23 @@ static std::string LabelFromValue(const UniValue& value) /** * Update coin control with fee estimation based on the given parameters * - * @param[in] pwallet Wallet pointer + * @param[in] wallet Wallet reference * @param[in,out] cc Coin control to be updated * @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h; - * if a fee_rate is present, 0 is allowed here as a no-op positional placeholder * @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative"; - * if a fee_rate is present, "" is allowed here as a no-op positional placeholder * @param[in] fee_rate UniValue real; fee rate in sat/vB; - * if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op + * if present, both conf_target and estimate_mode must either be null, or "unset" * @param[in] override_min_fee bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead * verify only that fee_rate is greater than 0 * @throws a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict */ -static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee) +static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee) { if (!fee_rate.isNull()) { - if (!conf_target.isNull() && conf_target.get_int() > 0) { + if (!conf_target.isNull()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate."); } - if (!estimate_mode.isNull() && !estimate_mode.get_str().empty()) { + if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate"); } cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN); @@ -228,7 +226,7 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage()); } if (!conf_target.isNull()) { - cc.m_confirm_target = ParseConfirmTarget(conf_target, pwallet->chain().estimateMaxBlocks()); + cc.m_confirm_target = ParseConfirmTarget(conf_target, wallet.chain().estimateMaxBlocks()); } } @@ -311,7 +309,7 @@ static RPCHelpMan getrawchangeaddress() throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys"); } - OutputType output_type = pwallet->m_default_change_type.get_value_or(pwallet->m_default_address_type); + OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type); if (!request.params[0].isNull()) { if (!ParseOutputType(request.params[0].get_str(), output_type)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str())); @@ -466,8 +464,8 @@ static RPCHelpMan sendtoaddress() + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1") + "\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments\n" + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"sean's outpost\" false true 6 economical") + - "\nSend 0.1 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB, subtract fee from amount, BIP125-replaceable, using positional arguments\n" - + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true true 0 \"\" 1") + + "\nSend 0.1 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB, subtract fee from amount, BIP125-replaceable, using positional arguments\n" + + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true true null \"unset\" null 1.1") + "\nSend 0.2 BTC with a confirmation target of 6 blocks in economical fee estimate mode using named arguments\n" + HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.2 conf_target=6 estimate_mode=\"economical\"") + "\nSend 0.5 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n" @@ -507,7 +505,7 @@ static RPCHelpMan sendtoaddress() // We also enable partial spend avoidance if reuse avoidance is set. coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse; - SetFeeEstimateMode(pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[9], /* override_min_fee */ false); + SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[9], /* override_min_fee */ false); EnsureWalletIsUnlocked(pwallet); @@ -629,7 +627,7 @@ static RPCHelpMan signmessage() throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } - const PKHash *pkhash = boost::get<PKHash>(&dest); + const PKHash* pkhash = std::get_if<PKHash>(&dest); if (!pkhash) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } @@ -935,7 +933,7 @@ static RPCHelpMan sendmany() coin_control.m_signal_bip125_rbf = request.params[5].get_bool(); } - SetFeeEstimateMode(pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[8], /* override_min_fee */ false); + SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[8], /* override_min_fee */ false); std::vector<CRecipient> recipients; ParseRecipients(sendTo, subtractFeeFromAmount, recipients); @@ -1575,8 +1573,7 @@ static RPCHelpMan listsinceblock() LOCK(wallet.cs_wallet); - // The way the 'height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0. - Optional<int> height = MakeOptional(false, int()); // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain. + Optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain. Optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain. int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; @@ -2539,7 +2536,7 @@ static RPCHelpMan listwalletdir() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { UniValue wallets(UniValue::VARR); - for (const auto& path : ListWalletDir()) { + for (const auto& path : ListDatabases(GetWalletDir())) { UniValue wallet(UniValue::VOBJ); wallet.pushKV("name", path.string()); wallets.push_back(wallet); @@ -2618,7 +2615,18 @@ static RPCHelpMan loadwallet() 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; + RPCErrorCode code = RPC_WALLET_ERROR; + switch (status) { + case DatabaseStatus::FAILED_NOT_FOUND: + case DatabaseStatus::FAILED_BAD_FORMAT: + code = RPC_WALLET_NOT_FOUND; + break; + case DatabaseStatus::FAILED_ALREADY_LOADED: + code = RPC_WALLET_ALREADY_LOADED; + break; + default: // RPC_WALLET_ERROR is returned for all other cases. + break; + } throw JSONRPCError(code, error.original); } @@ -2791,7 +2799,7 @@ static RPCHelpMan unloadwallet() "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n" "Specifying the wallet name on a wallet endpoint is invalid.", { - {"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC request", "The name of the wallet to unload."}, + {"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC endpoint", "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."}, {"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."}, }, RPCResult{RPCResult::Type::OBJ, "", "", { @@ -2805,8 +2813,8 @@ static RPCHelpMan unloadwallet() { std::string wallet_name; if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { - if (!request.params[0].isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot unload the requested wallet"); + if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets"); } } else { wallet_name = request.params[0].get_str(); @@ -3005,7 +3013,7 @@ static RPCHelpMan listunspent() std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey); if (provider) { if (scriptPubKey.IsPayToScriptHash()) { - const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address)); + const CScriptID& hash = CScriptID(std::get<ScriptHash>(address)); CScript redeemScript; if (provider->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript)); @@ -3015,7 +3023,7 @@ static RPCHelpMan listunspent() bool extracted = ExtractDestination(redeemScript, witness_destination); CHECK_NONFATAL(extracted); // Also return the witness script - const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination); + const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(witness_destination); CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; @@ -3025,7 +3033,7 @@ static RPCHelpMan listunspent() } } } else if (scriptPubKey.IsPayToWitnessScriptHash()) { - const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address); + const WitnessV0ScriptHash& whash = std::get<WitnessV0ScriptHash>(address); CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; @@ -3159,7 +3167,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f if (options.exists("replaceable")) { coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool(); } - SetFeeEstimateMode(pwallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee); + SetFeeEstimateMode(*pwallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee); } } else { // if options is null and not a bool @@ -3452,10 +3460,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name) 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; + throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead."); } RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ}); @@ -3487,7 +3492,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name) if (options.exists("replaceable")) { coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool(); } - SetFeeEstimateMode(pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /* override_min_fee */ false); + SetFeeEstimateMode(*pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /* override_min_fee */ false); } // Make sure the results are valid at least up to the most recent block @@ -3599,7 +3604,7 @@ static RPCHelpMan rescanblockchain() } int start_height = 0; - Optional<int> stop_height = MakeOptional(false, int()); + Optional<int> stop_height; uint256 start_block; { LOCK(pwallet->cs_wallet); @@ -3648,7 +3653,7 @@ static RPCHelpMan rescanblockchain() }; } -class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> +class DescribeWalletAddressVisitor { public: const SigningProvider * const provider; @@ -3667,7 +3672,7 @@ public: UniValue subobj(UniValue::VOBJ); UniValue detail = DescribeAddress(embedded); subobj.pushKVs(detail); - UniValue wallet_detail = boost::apply_visitor(*this, embedded); + UniValue wallet_detail = std::visit(*this, embedded); subobj.pushKVs(wallet_detail); subobj.pushKV("address", EncodeDestination(embedded)); subobj.pushKV("scriptPubKey", HexStr(subscript)); @@ -3750,7 +3755,7 @@ static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDes provider = pwallet->GetSolvingProvider(script); } ret.pushKVs(detail); - ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider.get()), dest)); + ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest)); return ret; } @@ -3826,13 +3831,19 @@ RPCHelpMan getaddressinfo() LOCK(pwallet->cs_wallet); - UniValue ret(UniValue::VOBJ); - CTxDestination dest = DecodeDestination(request.params[0].get_str()); + std::string error_msg; + CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg); + // Make sure the destination is valid if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + // Set generic error message in case 'DecodeDestination' didn't set it + if (error_msg.empty()) error_msg = "Invalid address"; + + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg); } + UniValue ret(UniValue::VOBJ); + std::string currentAddress = EncodeDestination(dest); ret.pushKV("address", currentAddress); @@ -4076,10 +4087,10 @@ static RPCHelpMan send() RPCExamples{"" "\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode\n" + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 6 economical\n") + - "Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB using positional arguments\n" - + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' 0 \"\" 1\n") + + "Send 0.2 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB using positional arguments\n" + + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" 1.1\n") + "Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB using the options argument\n" - + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' '{\"fee_rate\": 1}'\n") + + + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" null '{\"fee_rate\": 1}'\n") + "Send 0.3 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n" + HelpExampleCli("-named send", "outputs='{\"" + EXAMPLE_ADDRESS[0] + "\": 0.3}' fee_rate=25\n") + "Create 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" @@ -4091,7 +4102,7 @@ static RPCHelpMan send() UniValueType(), // outputs (ARR or OBJ, checked later) UniValue::VNUM, // conf_target UniValue::VSTR, // estimate_mode - UniValue::VNUM, // fee_rate + UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode() UniValue::VOBJ, // options }, true ); @@ -4236,7 +4247,7 @@ static RPCHelpMan sethdseed() // Do not do anything to non-HD wallets if (!pwallet->CanSupportFeature(FEATURE_HD)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD"); + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD"); } EnsureWalletIsUnlocked(pwallet); @@ -4467,14 +4478,18 @@ static RPCHelpMan walletcreatefundedpsbt() static RPCHelpMan upgradewallet() { return RPCHelpMan{"upgradewallet", - "\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n" + "\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n" "New keys may be generated and a new wallet backup will need to be made.", { - {"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version"} + {"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version."} }, RPCResult{ RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"}, + {RPCResult::Type::NUM, "previous_version", "Version of wallet before this operation"}, + {RPCResult::Type::NUM, "current_version", "Version of wallet after this operation"}, + {RPCResult::Type::STR, "result", /* optional */ true, "Description of result, if no error"}, {RPCResult::Type::STR, "error", /* optional */ true, "Error message (if there is one)"} }, }, @@ -4497,11 +4512,27 @@ static RPCHelpMan upgradewallet() version = request.params[0].get_int(); } bilingual_str error; - if (!pwallet->UpgradeWallet(version, error)) { - throw JSONRPCError(RPC_WALLET_ERROR, error.original); + const int previous_version{pwallet->GetVersion()}; + const bool wallet_upgraded{pwallet->UpgradeWallet(version, error)}; + const int current_version{pwallet->GetVersion()}; + std::string result; + + if (wallet_upgraded) { + if (previous_version == current_version) { + result = "Already at latest version. Wallet version unchanged."; + } else { + result = strprintf("Wallet upgraded successfully from version %i to version %i.", previous_version, current_version); + } } + UniValue obj(UniValue::VOBJ); - if (!error.empty()) { + obj.pushKV("wallet_name", pwallet->GetName()); + obj.pushKV("previous_version", previous_version); + obj.pushKV("current_version", current_version); + if (!result.empty()) { + obj.pushKV("result", result); + } else { + CHECK_NONFATAL(!error.empty()); obj.pushKV("error", error.original); } return obj; @@ -4520,73 +4551,75 @@ RPCHelpMan importprunedfunds(); RPCHelpMan removeprunedfunds(); RPCHelpMan importmulti(); RPCHelpMan importdescriptors(); +RPCHelpMan listdescriptors(); Span<const CRPCCommand> GetWalletRPCCommands() { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} }, - { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, - { "wallet", "abortrescan", &abortrescan, {} }, - { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} }, - { "wallet", "backupwallet", &backupwallet, {"destination"} }, - { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, - { "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"} }, - { "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} }, - { "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, - { "wallet", "getbalance", &getbalance, {"dummy","minconf","include_watchonly","avoid_reuse"} }, - { "wallet", "getnewaddress", &getnewaddress, {"label","address_type"} }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, - { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} }, - { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, - { "wallet", "getbalances", &getbalances, {} }, - { "wallet", "getwalletinfo", &getwalletinfo, {} }, - { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, - { "wallet", "importdescriptors", &importdescriptors, {"requests"} }, - { "wallet", "importmulti", &importmulti, {"requests","options"} }, - { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, - { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, - { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, {"filename"} }, - { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, - { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, - { "wallet", "listlabels", &listlabels, {"purpose"} }, - { "wallet", "listlockunspent", &listlockunspent, {} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} }, - { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, {"label|dummy","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, - { "wallet", "listwalletdir", &listwalletdir, {} }, - { "wallet", "listwallets", &listwallets, {} }, - { "wallet", "loadwallet", &loadwallet, {"filename", "load_on_startup"} }, - { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, - { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, - { "wallet", "send", &send, {"outputs","conf_target","estimate_mode","fee_rate","options"} }, - { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode","fee_rate","verbose"} }, - { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse","fee_rate","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", "load_on_startup"} }, - { "wallet", "upgradewallet", &upgradewallet, {"version"} }, - { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} }, - { "wallet", "walletlock", &walletlock, {} }, - { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, - { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} }, +{ // category actor (function) + // ------------------ ------------------------ + { "rawtransactions", &fundrawtransaction, }, + { "wallet", &abandontransaction, }, + { "wallet", &abortrescan, }, + { "wallet", &addmultisigaddress, }, + { "wallet", &backupwallet, }, + { "wallet", &bumpfee, }, + { "wallet", &psbtbumpfee, }, + { "wallet", &createwallet, }, + { "wallet", &dumpprivkey, }, + { "wallet", &dumpwallet, }, + { "wallet", &encryptwallet, }, + { "wallet", &getaddressesbylabel, }, + { "wallet", &getaddressinfo, }, + { "wallet", &getbalance, }, + { "wallet", &getnewaddress, }, + { "wallet", &getrawchangeaddress, }, + { "wallet", &getreceivedbyaddress, }, + { "wallet", &getreceivedbylabel, }, + { "wallet", &gettransaction, }, + { "wallet", &getunconfirmedbalance, }, + { "wallet", &getbalances, }, + { "wallet", &getwalletinfo, }, + { "wallet", &importaddress, }, + { "wallet", &importdescriptors, }, + { "wallet", &importmulti, }, + { "wallet", &importprivkey, }, + { "wallet", &importprunedfunds, }, + { "wallet", &importpubkey, }, + { "wallet", &importwallet, }, + { "wallet", &keypoolrefill, }, + { "wallet", &listaddressgroupings, }, + { "wallet", &listdescriptors, }, + { "wallet", &listlabels, }, + { "wallet", &listlockunspent, }, + { "wallet", &listreceivedbyaddress, }, + { "wallet", &listreceivedbylabel, }, + { "wallet", &listsinceblock, }, + { "wallet", &listtransactions, }, + { "wallet", &listunspent, }, + { "wallet", &listwalletdir, }, + { "wallet", &listwallets, }, + { "wallet", &loadwallet, }, + { "wallet", &lockunspent, }, + { "wallet", &removeprunedfunds, }, + { "wallet", &rescanblockchain, }, + { "wallet", &send, }, + { "wallet", &sendmany, }, + { "wallet", &sendtoaddress, }, + { "wallet", &sethdseed, }, + { "wallet", &setlabel, }, + { "wallet", &settxfee, }, + { "wallet", &setwalletflag, }, + { "wallet", &signmessage, }, + { "wallet", &signrawtransactionwithwallet, }, + { "wallet", &unloadwallet, }, + { "wallet", &upgradewallet, }, + { "wallet", &walletcreatefundedpsbt, }, + { "wallet", &walletlock, }, + { "wallet", &walletpassphrase, }, + { "wallet", &walletpassphrasechange, }, + { "wallet", &walletprocesspsbt, }, }; // clang-format on return MakeSpan(commands); diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp index da5ca7858f..09a9ec68cd 100644 --- a/src/wallet/salvage.cpp +++ b/src/wallet/salvage.cpp @@ -32,8 +32,9 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v 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); + BerkeleyDatabase& berkeley_database = static_cast<BerkeleyDatabase&>(*database); + std::string filename = berkeley_database.Filename(); + std::shared_ptr<BerkeleyEnvironment> env = berkeley_database.env; if (!env->Open(error)) { return false; diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 7ed20e4394..15972fe7bb 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -456,7 +456,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual hd_upgrade = true; } // Upgrade to HD chain split if necessary - if (IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) { + if (!IsFeatureSupported(prev_version, FEATURE_HD_SPLIT) && IsFeatureSupported(new_version, 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; diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 43791acfcf..8f6b69bc78 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -172,7 +172,7 @@ protected: WalletStorage& m_storage; public: - ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {} + explicit ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {} virtual ~ScriptPubKeyMan() {}; virtual bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { return false; } virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; } @@ -304,7 +304,7 @@ private: /* the HD chain data model (external chain counters) */ CHDChain m_hd_chain; - std::unordered_map<CKeyID, CHDChain, KeyIDHasher> m_inactive_hd_chains; + std::unordered_map<CKeyID, CHDChain, SaltedSipHasher> m_inactive_hd_chains; /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); @@ -504,7 +504,7 @@ class LegacySigningProvider : public SigningProvider private: const LegacyScriptPubKeyMan& m_spk_man; public: - LegacySigningProvider(const LegacyScriptPubKeyMan& spk_man) : m_spk_man(spk_man) {} + explicit LegacySigningProvider(const LegacyScriptPubKeyMan& spk_man) : m_spk_man(spk_man) {} bool GetCScript(const CScriptID &scriptid, CScript& script) const override { return m_spk_man.GetCScript(scriptid, script); } bool HaveCScript(const CScriptID &scriptid) const override { return m_spk_man.HaveCScript(scriptid); } diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index d278d96476..0fb3b1d3c4 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -17,7 +17,6 @@ #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; @@ -568,17 +567,11 @@ bool SQLiteBatch::TxnAbort() 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); + fs::path data_file = SQLiteDataFile(path); + auto db = MakeUnique<SQLiteDatabase>(data_file.parent_path(), data_file); if (options.verify && !db->Verify(error)) { status = DatabaseStatus::FAILED_VERIFY; return nullptr; @@ -596,37 +589,3 @@ 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 index 693a2ef55a..70ab4f797a 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -37,7 +37,7 @@ public: explicit SQLiteBatch(SQLiteDatabase& database); ~SQLiteBatch() override { Close(); } - /* No-op. See commeng on SQLiteDatabase::Flush */ + /* No-op. See comment on SQLiteDatabase::Flush */ void Flush() override {} void Close() override; @@ -113,10 +113,8 @@ public: 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 f38ccba384..ffac78d752 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -35,7 +35,7 @@ static CAmount balance = 0; CoinEligibilityFilter filter_standard(1, 6, 0); CoinEligibilityFilter filter_confirmed(1, 1, 0); CoinEligibilityFilter filter_standard_extra(6, 6, 0); -CoinSelectionParams coin_selection_params(false, 0, 0, CFeeRate(0), 0); +CoinSelectionParams coin_selection_params(false, 0, 0, CFeeRate(0), 0, false); static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set) { @@ -64,7 +64,8 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo if (spendable) { CTxDestination dest; std::string error; - assert(wallet.GetNewDestination(OutputType::BECH32, "", dest, error)); + const bool destination_ok = wallet.GetNewDestination(OutputType::BECH32, "", dest, error); + assert(destination_ok); tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest); } if (fIsFromMe) { @@ -114,7 +115,10 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<CInputCoin>& coins { static std::vector<OutputGroup> static_groups; static_groups.clear(); - for (auto& coin : coins) static_groups.emplace_back(coin, 0, true, 0, 0); + for (auto& coin : coins) { + static_groups.emplace_back(); + static_groups.back().Insert(coin, 0, true, 0, 0, false); + } return static_groups; } @@ -122,7 +126,10 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins) { static std::vector<OutputGroup> static_groups; static_groups.clear(); - for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0); + for (auto& coin : coins) { + static_groups.emplace_back(); + static_groups.back().Insert(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0, false); + } return static_groups; } @@ -262,28 +269,28 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) } // Make sure that effective value is working in SelectCoinsMinConf when BnB is used - CoinSelectionParams coin_selection_params_bnb(true, 0, 0, CFeeRate(3000), 0); + CoinSelectionParams coin_selection_params_bnb(true, 0, 0, CFeeRate(3000), 0, false); CoinSet setCoinsRet; CAmount nValueRet; bool bnb_used; empty_wallet(); add_coin(1); vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); // Test fees subtracted from output: empty_wallet(); add_coin(1 * CENT); vCoins.at(0).nInputBytes = 40; - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); coin_selection_params_bnb.m_subtract_fee_outputs = true; - BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // Make sure that can use BnB when there are preset inputs empty_wallet(); { - std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase()); + std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); bool firstRun; wallet->LoadWallet(firstRun); wallet->SetupLegacyScriptPubKeyMan(); @@ -316,24 +323,24 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) empty_wallet(); // with an empty wallet we can't even pay one cent - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); add_coin(1*CENT, 4); // add a new 1 cent coin // with a new 1 cent coin, we still can't find a mature 1 cent - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // but we can find a new 1 cent - BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); add_coin(2*CENT); // add a mature 2 cent coin // we can't make 3 cents of mature coins - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // we can make 3 cents of new coins - BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 3 * CENT); add_coin(5*CENT); // add a mature 5 cent coin, @@ -343,33 +350,33 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38 // we can't make 38 cents only if we disallow new coins: - BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // we can't even make 37 cents if we don't allow new coins even if they're from us - BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // but we can make 37 cents if we accept new coins from ourself - BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 37 * CENT); // and we can make 38 cents if we accept all new coins - BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 38 * CENT); // try making 34 cents from 1,2,5,10,20 - we can't do it exactly - BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible) // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5 - BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 7 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough. - BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK(nValueRet == 8 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10) - BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 10 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -383,30 +390,30 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total // check that we have 71 and not 72 - BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 - BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 - BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 - BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins // now try making 11 cents. we should get 5+6 - BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -415,11 +422,11 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin( 2*COIN); add_coin( 3*COIN); add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents - BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); - BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -434,14 +441,14 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly - BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // but if we add a bigger coin, small change is avoided add_coin(1111*MIN_CHANGE); // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 - BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // if we add more small coins: @@ -449,16 +456,16 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 7 / 10); // and try again to make 1.0 * MIN_CHANGE - BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount - // run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf) + // run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf) // they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change empty_wallet(); for (int j = 0; j < 20; j++) add_coin(50000 * COIN); - BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins @@ -471,7 +478,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 6 / 10); add_coin(MIN_CHANGE * 7 / 10); add_coin(1111 * MIN_CHANGE); - BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -481,7 +488,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 6 / 10); add_coin(MIN_CHANGE * 8 / 10); add_coin(1111 * MIN_CHANGE); - BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6 @@ -492,12 +499,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 100); // trying to make 100.01 from these three coins - BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change - BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); } @@ -511,7 +518,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times. for (int i = 0; i < RUN_TESTS; i++) { - BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); if (amt - 2000 < MIN_CHANGE) { // needs more than one input: @@ -537,8 +544,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) for (int i = 0; i < RUN_TESTS; i++) { // picking 50 from 100 coins doesn't depend on the shuffle, // but does depend on randomness in the stochastic approximation code - BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2)); int fails = 0; @@ -546,8 +553,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) { // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time // run the test RANDOM_REPEATS times and only complain if all of them fail - BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, GroupCoins(vCoins), setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); if (equal_sets(setCoinsRet, setCoinsRet2)) fails++; } @@ -569,8 +576,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) { // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time // run the test RANDOM_REPEATS times and only complain if all of them fail - BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, GroupCoins(vCoins), setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); if (equal_sets(setCoinsRet, setCoinsRet2)) fails++; } @@ -597,7 +604,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) add_coin(1000 * COIN); add_coin(3 * COIN); - BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -632,13 +639,13 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) CAmount target = rand.randrange(balance - 1000) + 1000; // Perform selection - CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0); - CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0); + CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0, false); + CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0, false); CoinSet out_set; CAmount out_value = 0; bool bnb_used = false; - BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, GroupCoins(vCoins), out_set, out_value, coin_selection_params_bnb, bnb_used) || - testWallet.SelectCoinsMinConf(target, filter_standard, GroupCoins(vCoins), out_set, out_value, coin_selection_params_knapsack, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_bnb, bnb_used) || + testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_knapsack, bnb_used)); BOOST_CHECK_GE(out_value, target); } } diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index 8f0083cd2e..27179839b7 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,6 +13,13 @@ BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup) +static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, std::string& database_filename) +{ + fs::path data_file = BDBDataFile(path); + database_filename = data_file.filename().string(); + return GetBerkeleyEnv(data_file.parent_path()); +} + BOOST_AUTO_TEST_CASE(getwalletenv_file) { std::string test_name = "test_name.dat"; diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp index c80310045a..f035a70a20 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -1,8 +1,9 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <fs.h> +#include <univalue.h> #include <util/check.h> #include <util/system.h> @@ -10,7 +11,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) { - m_wallet_client = MakeWalletClient(*m_chain, *Assert(m_node.args)); + m_wallet_client = MakeWalletClient(*m_node.chain, *Assert(m_node.args)); std::string sep; sep += fs::path::preferred_separator; @@ -37,6 +38,9 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam InitWalletDirTestingSetup::~InitWalletDirTestingSetup() { + gArgs.LockSettings([&](util::Settings& settings) { + settings.forced_settings.erase("walletdir"); + }); fs::current_path(m_cwd); } diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h index f5bade77df..37ae907de5 100644 --- a/src/wallet/test/init_test_fixture.h +++ b/src/wallet/test/init_test_fixture.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,7 +19,6 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup { fs::path m_datadir; 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::WalletClient> m_wallet_client; }; diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp index 9b905569fc..e70b56c529 100644 --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019 The Bitcoin Core developers +// Copyright (c) 2018-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp index d5aed99d99..5d25885bd4 100644 --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -27,8 +27,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard) CKey uncompressedKey; uncompressedKey.MakeNewKey(false); CPubKey uncompressedPubkey = uncompressedKey.GetPubKey(); - NodeContext node; - std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node); + std::unique_ptr<interfaces::Chain>& chain = m_node.chain; CScript scriptPubKey; isminetype result; diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp index f7c1337b0d..347a436429 100644 --- a/src/wallet/test/scriptpubkeyman_tests.cpp +++ b/src/wallet/test/scriptpubkeyman_tests.cpp @@ -17,9 +17,7 @@ BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup) 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(), "", CreateDummyWalletDatabase()); + CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan(); // Make a 1 of 2 multisig script diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index 10ddfa22ef..5b421840e0 100644 --- a/src/wallet/test/wallet_crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 The Bitcoin Core developers +// Copyright (c) 2014-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 4d6f427618..badf2eb459 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(), "", CreateMockWalletDatabase()) + m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase()) { bool fFirstRun; m_wallet.LoadWallet(fFirstRun); - m_chain_notifications_handler = m_chain->handleNotifications({ &m_wallet, [](CWallet*) {} }); + m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} }); m_wallet_client->registerRpcs(); } diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index ba8a5ff1f3..ab7fb8c42b 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -20,8 +20,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::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args)); + std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_node.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 4911af08c6..5480f3ab22 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -28,6 +28,8 @@ RPCHelpMan importmulti(); RPCHelpMan dumpwallet(); RPCHelpMan importwallet(); +extern RecursiveMutex cs_wallets; + // Ensure that fee levels defined in the wallet are at least as high // as the default levels for node policy. static_assert(DEFAULT_TRANSACTION_MINFEE >= DEFAULT_MIN_RELAY_TX_FEE, "wallet minimum fee is smaller than default relay fee"); @@ -83,12 +85,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = ::ChainActive().Tip(); - NodeContext node; - auto chain = interfaces::MakeChain(node); - // Verify ScanForWalletTransactions fails to read an unknown start block. { - CWallet wallet(chain.get(), "", CreateDummyWalletDatabase()); + CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); { LOCK(wallet.cs_wallet); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); @@ -107,7 +106,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(), "", CreateDummyWalletDatabase()); + CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); { LOCK(wallet.cs_wallet); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); @@ -133,7 +132,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) // Verify ScanForWalletTransactions only picks transactions in the new block // file. { - CWallet wallet(chain.get(), "", CreateDummyWalletDatabase()); + CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); { LOCK(wallet.cs_wallet); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); @@ -158,7 +157,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) // Verify ScanForWalletTransactions scans no blocks. { - CWallet wallet(chain.get(), "", CreateDummyWalletDatabase()); + CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); { LOCK(wallet.cs_wallet); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); @@ -183,9 +182,6 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup) CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = ::ChainActive().Tip(); - NodeContext node; - auto chain = interfaces::MakeChain(node); - // Prune the older block file. { LOCK(cs_main); @@ -197,7 +193,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(), "", CreateDummyWalletDatabase()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); wallet->SetupLegacyScriptPubKeyMan(); WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash())); AddWallet(wallet); @@ -255,14 +251,11 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) SetMockTime(KEY_TIME); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); - NodeContext node; - auto chain = interfaces::MakeChain(node); - std::string backup_file = (GetDataDir() / "wallet.backup").string(); // Import key into wallet and call dumpwallet to create backup file. { - std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); { auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan(); LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore); @@ -284,7 +277,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // 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(), "", CreateDummyWalletDatabase()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); LOCK(wallet->cs_wallet); wallet->SetupLegacyScriptPubKeyMan(); @@ -317,10 +310,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // debit functions. BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) { - NodeContext node; - auto chain = interfaces::MakeChain(node); - - CWallet wallet(chain.get(), "", CreateDummyWalletDatabase()); + CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan(); CWalletTx wtx(&wallet, m_coinbase_txns.back()); @@ -495,7 +485,7 @@ public: ListCoinsTestingSetup() { CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); - wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase()); + wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); { LOCK2(wallet->cs_wallet, ::cs_main); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); @@ -545,7 +535,6 @@ public: return it->second; } - std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node); std::unique_ptr<CWallet> wallet; }; @@ -561,7 +550,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); // Check initial balance from one mature coinbase transaction. @@ -577,7 +566,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); // Lock both coins. Confirm number of available coins drops to 0. @@ -606,15 +595,13 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); } 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(), "", CreateDummyWalletDatabase()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase()); wallet->SetupLegacyScriptPubKeyMan(); wallet->SetMinVersion(FEATURE_LATEST); wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); @@ -709,8 +696,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup) BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup) { // Create new wallet with known key and unload it. - auto chain = interfaces::MakeChain(m_node); - auto wallet = TestLoadWallet(*chain); + auto wallet = TestLoadWallet(*m_node.chain); CKey key; key.MakeNewKey(true); AddKey(*wallet, key); @@ -745,12 +731,12 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup) auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey())); m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey())); - BOOST_CHECK(chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error)); + BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error)); // Reload wallet and make sure new transactions are detected despite events // being blocked - wallet = TestLoadWallet(*chain); + wallet = TestLoadWallet(*m_node.chain); BOOST_CHECK(rescan_completed); BOOST_CHECK_EQUAL(addtx_count, 2); { @@ -777,18 +763,20 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup) // deadlock during the sync and simulates a new block notification happening // as soon as possible. addtx_count = 0; - auto handler = HandleLoadWallet([&](std::unique_ptr<interfaces::Wallet> wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->wallet()->cs_wallet) { + auto handler = HandleLoadWallet([&](std::unique_ptr<interfaces::Wallet> wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet->wallet()->cs_wallet, cs_wallets) { BOOST_CHECK(rescan_completed); m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey())); m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey())); - BOOST_CHECK(chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error)); + BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error)); + LEAVE_CRITICAL_SECTION(cs_wallets); LEAVE_CRITICAL_SECTION(wallet->wallet()->cs_wallet); SyncWithValidationInterfaceQueue(); ENTER_CRITICAL_SECTION(wallet->wallet()->cs_wallet); + ENTER_CRITICAL_SECTION(cs_wallets); }); - wallet = TestLoadWallet(*chain); + wallet = TestLoadWallet(*m_node.chain); BOOST_CHECK_EQUAL(addtx_count, 4); { LOCK(wallet->cs_wallet); @@ -802,8 +790,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup) { - auto chain = interfaces::MakeChain(m_node); - auto wallet = TestLoadWallet(*chain); + auto wallet = TestLoadWallet(*m_node.chain); CKey key; key.MakeNewKey(true); AddKey(*wallet, key); diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp index a3859e2e4b..558121ae42 100644 --- a/src/wallet/test/walletdb_tests.cpp +++ b/src/wallet/test/walletdb_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 The Bitcoin Core developers +// Copyright (c) 2012-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ff8bfff872..db80745db0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -52,7 +52,7 @@ const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{ static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; -static RecursiveMutex cs_wallets; +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); @@ -85,9 +85,9 @@ static void UpdateWalletSetting(interfaces::Chain& chain, std::vector<bilingual_str>& warnings) { if (load_on_startup == nullopt) return; - if (load_on_startup.get() && !AddWalletSetting(chain, wallet_name)) { + if (load_on_startup.value() && !AddWalletSetting(chain, wallet_name)) { warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup.")); - } else if (!load_on_startup.get() && !RemoveWalletSetting(chain, wallet_name)) { + } else if (!load_on_startup.value() && !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.")); } } @@ -419,7 +419,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey)) return false; - WalletBatch(*database).WriteMasterKey(pMasterKey.first, pMasterKey.second); + WalletBatch(GetDatabase()).WriteMasterKey(pMasterKey.first, pMasterKey.second); if (fWasLocked) Lock(); return true; @@ -432,7 +432,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, void CWallet::chainStateFlushed(const CBlockLocator& loc) { - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); batch.WriteBestBlock(loc); } @@ -444,7 +444,7 @@ void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in) nWalletVersion = nVersion; { - WalletBatch* batch = batch_in ? batch_in : new WalletBatch(*database); + WalletBatch* batch = batch_in ? batch_in : new WalletBatch(GetDatabase()); if (nWalletVersion > 40000) batch->WriteMinVersion(nWalletVersion); if (!batch_in) @@ -484,12 +484,12 @@ bool CWallet::HasWalletSpend(const uint256& txid) const void CWallet::Flush() { - database->Flush(); + GetDatabase().Flush(); } void CWallet::Close() { - database->Close(); + GetDatabase().Close(); } void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range) @@ -570,7 +570,7 @@ void CWallet::AddToSpends(const uint256& wtxid) { auto it = mapWallet.find(wtxid); assert(it != mapWallet.end()); - CWalletTx& thisTx = it->second; + const CWalletTx& thisTx = it->second; if (thisTx.IsCoinBase()) // Coinbases don't spend anything! return; @@ -615,7 +615,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; - WalletBatch* encrypted_batch = new WalletBatch(*database); + WalletBatch* encrypted_batch = new WalletBatch(GetDatabase()); if (!encrypted_batch->TxnBegin()) { delete encrypted_batch; encrypted_batch = nullptr; @@ -667,12 +667,12 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) // Need to completely rewrite the wallet file; if we don't, bdb might keep // bits of the unencrypted private key in slack space in the database file. - database->Rewrite(); + GetDatabase().Rewrite(); // BDB seems to have a bad habit of writing old data into // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: - database->ReloadDbEnv(); + GetDatabase().ReloadDbEnv(); } NotifyStatusChanged(this); @@ -683,7 +683,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) DBErrors CWallet::ReorderTransactions() { LOCK(cs_wallet); - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); // Old wallets didn't have any defined order for transactions // Probably a bad idea to change the output of this @@ -744,7 +744,7 @@ int64_t CWallet::IncOrderPosNext(WalletBatch* batch) if (batch) { batch->WriteOrderPosNext(nOrderPosNext); } else { - WalletBatch(*database).WriteOrderPosNext(nOrderPosNext); + WalletBatch(GetDatabase()).WriteOrderPosNext(nOrderPosNext); } return nRet; } @@ -774,7 +774,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) wtx.mapValue["replaced_by_txid"] = newHash.ToString(); - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); bool success = true; if (!batch.WriteTx(wtx)) { @@ -846,7 +846,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio { LOCK(cs_wallet); - WalletBatch batch(*database, fFlushOnClose); + WalletBatch batch(GetDatabase(), fFlushOnClose); uint256 hash = tx->GetHash(); @@ -946,11 +946,12 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx } // If wallet doesn't have a chain (e.g wallet-tool), don't bother to update txn. if (HaveChain()) { - Optional<int> block_height = chain().getBlockHeight(wtx.m_confirm.hashBlock); - if (block_height) { + bool active; + int height; + if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().inActiveChain(active).height(height)) && active) { // Update cached block height variable since it not stored in the // serialized transaction. - wtx.m_confirm.block_height = *block_height; + wtx.m_confirm.block_height = height; } else if (wtx.isConflicted() || wtx.isConfirmed()) { // If tx block (or conflicting block) was reorged out of chain // while the wallet was shutdown, change tx status to UNCONFIRMED @@ -1045,7 +1046,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) { LOCK(cs_wallet); - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); std::set<uint256> todo; std::set<uint256> done; @@ -1053,7 +1054,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) // Can't mark abandoned if confirmed or in mempool auto it = mapWallet.find(hashTx); assert(it != mapWallet.end()); - CWalletTx& origtx = it->second; + const CWalletTx& origtx = it->second; if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) { return false; } @@ -1108,7 +1109,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c return; // Do not flush the wallet here for performance reasons - WalletBatch batch(*database, false); + WalletBatch batch(GetDatabase(), false); std::set<uint256> todo; std::set<uint256> done; @@ -1177,9 +1178,8 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe // Handle transactions that were removed from the mempool because they // conflict with transactions in a newly connected block. if (reason == MemPoolRemovalReason::CONFLICT) { - // Call SyncNotifications, so external -walletnotify notifications will - // be triggered for these transactions. Set Status::UNCONFIRMED instead - // of Status::CONFLICTED for a few reasons: + // Trigger external -walletnotify notifications for these transactions. + // Set Status::UNCONFIRMED instead of Status::CONFLICTED for a few reasons: // // 1. The transactionRemovedFromMempool callback does not currently // provide the conflicting block's hash and height, and for backwards @@ -1446,13 +1446,13 @@ void CWallet::SetWalletFlag(uint64_t flags) { LOCK(cs_wallet); m_wallet_flags |= flags; - if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) + if (!WalletBatch(GetDatabase()).WriteWalletFlags(m_wallet_flags)) throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed"); } void CWallet::UnsetWalletFlag(uint64_t flag) { - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); UnsetWalletFlagWithDB(batch, flag); } @@ -1491,7 +1491,7 @@ bool CWallet::AddWalletFlags(uint64_t flags) LOCK(cs_wallet); // We should never be writing unknown non-tolerable wallet flags assert(((flags & KNOWN_WALLET_FLAGS) >> 32) == (flags >> 32)); - if (!WalletBatch(*database).WriteWalletFlags(flags)) { + if (!WalletBatch(GetDatabase()).WriteWalletFlags(flags)) { throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed"); } @@ -1582,7 +1582,7 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri return false; } if (apply_label) { - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); for (const CScript& script : script_pub_keys) { CTxDestination dest; ExtractDestination(script, dest); @@ -1771,18 +1771,22 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current); } + // Read block data CBlock block; - bool next_block; + chain().findBlock(block_hash, FoundBlock().data(block)); + + // Find next block separately from reading data above, because reading + // is slow and there might be a reorg while it is read. + bool block_still_active = false; + bool next_block = false; uint256 next_block_hash; - bool reorg = false; - if (chain().findBlock(block_hash, FoundBlock().data(block)) && !block.IsNull()) { + chain().findBlock(block_hash, FoundBlock().inActiveChain(block_still_active).nextBlock(FoundBlock().inActiveChain(next_block).hash(next_block_hash))); + + if (!block.IsNull()) { LOCK(cs_wallet); - next_block = chain().findNextBlock(block_hash, block_height, FoundBlock().hash(next_block_hash), &reorg); - if (reorg) { + if (!block_still_active) { // Abort scan if current block is no longer active, to prevent // marking transactions as coming from the wrong block. - // TODO: This should return success instead of failure, see - // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518 result.last_failed_block = block_hash; result.status = ScanResult::FAILURE; break; @@ -1797,13 +1801,12 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc // could not scan block, keep scanning but record this block as the most recent failure result.last_failed_block = block_hash; result.status = ScanResult::FAILURE; - next_block = chain().findNextBlock(block_hash, block_height, FoundBlock().hash(next_block_hash), &reorg); } if (max_height && block_height >= *max_height) { break; } { - if (!next_block || reorg) { + if (!next_block) { // break successfully when rescan has reached the tip, or // previous block is no longer on the chain due to a reorg break; @@ -2354,13 +2357,12 @@ const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int out return ptx->vout[n]; } -bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups, +bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const { setCoinsRet.clear(); nValueRet = 0; - std::vector<OutputGroup> utxo_pool; if (coin_selection_params.use_bnb) { // Get long term estimate FeeCalculation feeCalc; @@ -2368,35 +2370,27 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil temp.m_confirm_target = 1008; CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc); - // Calculate cost of change - CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size); + // Get the feerate for effective value. + // When subtracting the fee from the outputs, we want the effective feerate to be 0 + CFeeRate effective_feerate{0}; + if (!coin_selection_params.m_subtract_fee_outputs) { + effective_feerate = coin_selection_params.effective_fee; + } - // Filter by the min conf specs and add to utxo_pool and calculate effective value - for (OutputGroup& group : groups) { - if (!group.EligibleForSpending(eligibility_filter)) continue; + std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, long_term_feerate, eligibility_filter, true /* positive_only */); - 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); - } + // Calculate cost of change + CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size); - 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); bnb_used = true; - return SelectCoinsBnB(utxo_pool, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees); + return SelectCoinsBnB(groups, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees); } else { - // Filter by the min conf specs and add to utxo_pool - for (const OutputGroup& group : groups) { - if (!group.EligibleForSpending(eligibility_filter)) continue; - utxo_pool.push_back(group); - } + std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, CFeeRate(0), CFeeRate(0), eligibility_filter, false /* positive_only */); + bnb_used = false; - return KnapsackSolver(nTargetValue, utxo_pool, setCoinsRet, nValueRet); + return KnapsackSolver(nTargetValue, groups, setCoinsRet, nValueRet); } } @@ -2479,16 +2473,14 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm // explicitly shuffling the outputs before processing Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext()); } - std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends, max_ancestors); - bool res = value_to_select <= 0 || - SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || - SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || + SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset util::insert(setCoinsRet, setPresetCoins); @@ -2706,7 +2698,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin return locktime; } -OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) +OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const { // If -changetype is specified, always use that change type. if (change_type) { @@ -2779,6 +2771,7 @@ bool CWallet::CreateTransactionInternal( std::vector<COutput> vAvailableCoins; AvailableCoins(vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy + coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; // Create change script that will be used if we need change // TODO: pass in scriptChange instead of reservedest so @@ -2786,7 +2779,7 @@ bool CWallet::CreateTransactionInternal( CScript scriptChange; // coin control: send change to custom address - if (!boost::get<CNoDestination>(&coin_control.destChange)) { + if (!std::get_if<CNoDestination>(&coin_control.destChange)) { scriptChange = GetScriptForDestination(coin_control.destChange); } else { // no coin control: send change to newly generated address // Note: We use a new key here to keep it from being obvious which side is the change. @@ -3048,7 +3041,7 @@ bool CWallet::CreateTransactionInternal( // to avoid conflicting with other possible uses of nSequence, // and in the spirit of "smallest possible change from prior // behavior." - const uint32_t nSequence = coin_control.m_signal_bip125_rbf.get_value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); + const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); for (const auto& coin : selected_coins) { txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); } @@ -3109,13 +3102,14 @@ bool CWallet::CreateTransaction( bool sign) { int nChangePosIn = nChangePosInOut; - CTransactionRef tx2 = tx; + Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr) bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign); // try with avoidpartialspends unless it's enabled already if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) { CCoinControl tmp_cc = coin_control; tmp_cc.m_avoid_partial_spends = true; CAmount nFeeRet2; + CTransactionRef tx2; int nChangePosInOut2 = nChangePosIn; bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) { @@ -3177,10 +3171,10 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) LOCK(cs_wallet); fFirstRunRet = false; - DBErrors nLoadWalletRet = WalletBatch(*database).LoadWallet(this); + DBErrors nLoadWalletRet = WalletBatch(GetDatabase()).LoadWallet(this); if (nLoadWalletRet == DBErrors::NEED_REWRITE) { - if (database->Rewrite("\x04pool")) + if (GetDatabase().Rewrite("\x04pool")) { for (const auto& spk_man_pair : m_spk_managers) { spk_man_pair.second->RewriteDB(); @@ -3204,7 +3198,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) { AssertLockHeld(cs_wallet); - DBErrors nZapSelectTxRet = WalletBatch(*database).ZapSelectTx(vHashIn, vHashOut); + DBErrors nZapSelectTxRet = WalletBatch(GetDatabase()).ZapSelectTx(vHashIn, vHashOut); for (const uint256& hash : vHashOut) { const auto& it = mapWallet.find(hash); wtxOrdered.erase(it->second.m_it_wtxOrdered); @@ -3216,7 +3210,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { - if (database->Rewrite("\x04pool")) + if (GetDatabase().Rewrite("\x04pool")) { for (const auto& spk_man_pair : m_spk_managers) { spk_man_pair.second->RewriteDB(); @@ -3254,14 +3248,14 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose) { - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); return SetAddressBookWithDB(batch, address, strName, strPurpose); } bool CWallet::DelAddressBook(const CTxDestination& address) { bool is_mine; - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); { 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) @@ -3720,7 +3714,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key, const std::string &value) { - if (boost::get<CNoDestination>(&dest)) + if (std::get_if<CNoDestination>(&dest)) return false; m_address_book[dest].destdata.insert(std::make_pair(key, value)); @@ -3776,7 +3770,7 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons // 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. - const fs::path& wallet_path = fs::absolute(name, GetWalletDir()); + const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), name); 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)) || @@ -4008,7 +4002,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st int rescan_height = 0; if (!gArgs.GetBoolArg("-rescan", false)) { - WalletBatch batch(*walletInstance->database); + WalletBatch batch(walletInstance->GetDatabase()); CBlockLocator locator; if (batch.ReadBestBlock(locator)) { if (const Optional<int> fork_height = chain.findLocatorFork(locator)) { @@ -4051,16 +4045,13 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st // No need to read and scan block if block was created before // our wallet birthday (as adjusted for block time variability) - // The way the 'time_first_key' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0. - Optional<int64_t> time_first_key = MakeOptional(false, int64_t());; + Optional<int64_t> time_first_key; for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) { int64_t time = spk_man->GetTimeFirstKey(); if (!time_first_key || time < *time_first_key) time_first_key = time; } if (time_first_key) { - if (Optional<int> first_block = chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, nullptr)) { - rescan_height = *first_block; - } + chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, FoundBlock().height(rescan_height)); } { @@ -4071,7 +4062,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st } } walletInstance->chainStateFlushed(chain.getTipLocator()); - walletInstance->database->IncrementUpdateCounter(); + walletInstance->GetDatabase().IncrementUpdateCounter(); } { @@ -4111,9 +4102,8 @@ bool CWallet::UpgradeWallet(int version, bilingual_str& error) } else { WalletLogPrintf("Allowing wallet upgrade up to %i\n", version); } - if (version < prev_version) - { - error = _("Cannot downgrade wallet"); + if (version < prev_version) { + error = strprintf(_("Cannot downgrade wallet from version %i to version %i. Wallet version unchanged."), prev_version, version); return false; } @@ -4121,7 +4111,7 @@ bool CWallet::UpgradeWallet(int version, bilingual_str& error) // Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT if (!CanSupportFeature(FEATURE_HD_SPLIT) && version >= FEATURE_HD_SPLIT && version < FEATURE_PRE_SPLIT_KEYPOOL) { - error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified."); + error = strprintf(_("Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified."), prev_version, version, FEATURE_PRE_SPLIT_KEYPOOL); return false; } @@ -4150,7 +4140,7 @@ void CWallet::postInitProcess() bool CWallet::BackupWallet(const std::string& strDest) const { - return database->Backup(strDest); + return GetDatabase().Backup(strDest); } CKeyPool::CKeyPool() @@ -4192,51 +4182,90 @@ bool CWalletTx::IsImmatureCoinBase() const return GetBlocksToMaturity() > 0; } -std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin, const size_t max_ancestors) const { - std::vector<OutputGroup> groups; - std::map<CTxDestination, OutputGroup> gmap; - std::set<CTxDestination> full_groups; +std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool separate_coins, const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate, const CoinEligibilityFilter& filter, bool positive_only) const +{ + std::vector<OutputGroup> groups_out; - for (const auto& output : outputs) { - if (output.fSpendable) { - CTxDestination dst; - CInputCoin input_coin = output.GetInputCoin(); + if (separate_coins) { + // Single coin means no grouping. Each COutput gets its own OutputGroup. + for (const COutput& output : outputs) { + // Skip outputs we cannot spend + if (!output.fSpendable) continue; size_t ancestors, descendants; chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); - if (!single_coin && ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey, dst)) { - auto it = gmap.find(dst); - if (it != gmap.end()) { - // Limit output groups to no more than OUTPUT_GROUP_MAX_ENTRIES - // number of entries, to protect against inadvertently creating - // a too-large transaction when using -avoidpartialspends to - // prevent breaking consensus or surprising users with a very - // high amount of fees. - if (it->second.m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) { - groups.push_back(it->second); - it->second = OutputGroup{}; - full_groups.insert(dst); - } - it->second.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants); - } else { - gmap[dst].Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants); - } - } else { - groups.emplace_back(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants); - } + CInputCoin input_coin = output.GetInputCoin(); + + // Make an OutputGroup containing just this output + OutputGroup group{effective_feerate, long_term_feerate}; + group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); + + // Check the OutputGroup's eligibility. Only add the eligible ones. + if (positive_only && group.effective_value <= 0) continue; + if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); + } + return groups_out; + } + + // We want to combine COutputs that have the same scriptPubKey into single OutputGroups + // except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup. + // To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups. + // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added + // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has + // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector. + std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map; + for (const auto& output : outputs) { + // Skip outputs we cannot spend + if (!output.fSpendable) continue; + + size_t ancestors, descendants; + chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); + CInputCoin input_coin = output.GetInputCoin(); + CScript spk = input_coin.txout.scriptPubKey; + + std::vector<OutputGroup>& groups = spk_to_groups_map[spk]; + + if (groups.size() == 0) { + // No OutputGroups for this scriptPubKey yet, add one + groups.emplace_back(effective_feerate, long_term_feerate); + } + + // Get the last OutputGroup in the vector so that we can add the CInputCoin to it + // A pointer is used here so that group can be reassigned later if it is full. + OutputGroup* group = &groups.back(); + + // Check if this OutputGroup is full. We limit to OUTPUT_GROUP_MAX_ENTRIES when using -avoidpartialspends + // to avoid surprising users with very high fees. + if (group->m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) { + // The last output group is full, add a new group to the vector and use that group for the insertion + groups.emplace_back(effective_feerate, long_term_feerate); + group = &groups.back(); } + + // Add the input_coin to group + group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); } - if (!single_coin) { - for (auto& it : gmap) { - auto& group = it.second; - if (full_groups.count(it.first) > 0) { - // Make this unattractive as we want coin selection to avoid it if possible - group.m_ancestors = max_ancestors - 1; + + // Now we go through the entire map and pull out the OutputGroups + for (const auto& spk_and_groups_pair: spk_to_groups_map) { + const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second; + + // Go through the vector backwards. This allows for the first item we deal with being the partial group. + for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) { + const OutputGroup& group = *group_it; + + // Don't include partial groups if there are full groups too and we don't want partial groups + if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) { + continue; } - groups.push_back(group); + + // Check the OutputGroup's eligibility. Only add the eligible ones. + if (positive_only && group.effective_value <= 0) continue; + if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); } } - return groups; + + return groups_out; } bool CWallet::IsCrypted() const @@ -4453,7 +4482,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans() void CWallet::AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal) { - WalletBatch batch(*database); + WalletBatch batch(GetDatabase()); if (!batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(type), id, internal)) { throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed"); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 69cf6b66a4..4fc4466604 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -610,8 +610,16 @@ struct CoinSelectionParams size_t tx_noinputs_size = 0; //! Indicate that we are subtracting the fee from outputs bool m_subtract_fee_outputs = false; - - CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {} + bool m_avoid_partial_spends = false; + + CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size, bool avoid_partial) : + use_bnb(use_bnb), + change_output_size(change_output_size), + change_spend_size(change_spend_size), + effective_fee(effective_fee), + tx_noinputs_size(tx_noinputs_size), + m_avoid_partial_spends(avoid_partial) + {} CoinSelectionParams() {} }; @@ -695,7 +703,7 @@ private: std::string m_name; /** Internal database handle. */ - std::unique_ptr<WalletDatabase> database; + std::unique_ptr<WalletDatabase> const m_database; /** * The following is used to keep track of how far behind the wallet is @@ -729,14 +737,11 @@ public: */ mutable RecursiveMutex cs_wallet; - /** Get database handle used by this wallet. Ideally this function would - * not be necessary. - */ - WalletDatabase& GetDBHandle() + WalletDatabase& GetDatabase() const override { - return *database; + assert(static_cast<bool>(m_database)); + return *m_database; } - WalletDatabase& GetDatabase() const override { return *database; } /** * Select a set of coins such that nValueRet >= nTargetValue and at least @@ -758,7 +763,7 @@ public: CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database) : m_chain(chain), m_name(name), - database(std::move(database)) + m_database(std::move(database)) { } @@ -821,7 +826,7 @@ public: * completion the coin set and corresponding actual target value is * assembled */ - bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups, + bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const; bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -830,7 +835,7 @@ public: bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin, const size_t max_ancestors) const; + std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool separate_coins, const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate, const CoinEligibilityFilter& filter, bool positive_only) const; bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -933,7 +938,7 @@ public: Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const; CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const; - OutputType TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend); + OutputType TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const; /** * Insert additional inputs into the transaction by diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 45807ae6fd..4e6270220e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -426,7 +426,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, uint256 checksum; ssValue >> checksum; if ((checksum_valid = Hash(vchPrivKey) != checksum)) { - strErr = "Error reading wallet database: Crypted key corrupt"; + strErr = "Error reading wallet database: Encrypted key corrupt"; return false; } } @@ -945,7 +945,7 @@ void MaybeCompactWalletDB() } for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { - WalletDatabase& dbh = pwallet->GetDBHandle(); + WalletDatabase& dbh = pwallet->GetDatabase(); unsigned int nUpdateCounter = dbh.nUpdateCounter; @@ -1013,13 +1013,10 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas Optional<DatabaseFormat> format; if (exists) { -#ifdef USE_BDB - if (ExistsBerkeleyDatabase(path)) { + if (IsBDBFile(BDBDataFile(path))) { format = DatabaseFormat::BERKELEY; } -#endif -#ifdef USE_SQLITE - if (ExistsSQLiteDatabase(path)) { + if (IsSQLiteFile(SQLiteDataFile(path))) { if (format) { error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", path.string())); status = DatabaseStatus::FAILED_BAD_FORMAT; @@ -1027,7 +1024,6 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas } format = DatabaseFormat::SQLITE; } -#endif } 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; diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index dad1232e10..bc90491a2c 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -5,6 +5,7 @@ #include <fs.h> #include <util/system.h> #include <util/translation.h> +#include <wallet/dump.h> #include <wallet/salvage.h> #include <wallet/wallet.h> #include <wallet/walletutil.h> @@ -21,30 +22,27 @@ static void WalletToolReleaseWallet(CWallet* wallet) delete wallet; } -static void WalletCreate(CWallet* wallet_instance) +static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flags) { LOCK(wallet_instance->cs_wallet); wallet_instance->SetMinVersion(FEATURE_HD_SPLIT); + wallet_instance->AddWalletFlags(wallet_creation_flags); - // generate a new HD seed - auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan(); - CPubKey seed = spk_man->GenerateNewSeed(); - spk_man->SetHDSeed(seed); + if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan(); + spk_man->SetupGeneration(false); + } else { + wallet_instance->SetupDescriptorScriptPubKeyMans(); + } tfm::format(std::cout, "Topping up keypool...\n"); wallet_instance->TopUpKeyPool(); } -static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, bool create) +static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options) { - 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) { @@ -85,7 +83,7 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa } } - if (create) WalletCreate(wallet_instance.get()); + if (options.require_create) WalletCreate(wallet_instance.get(), options.create_flags); return wallet_instance; } @@ -105,41 +103,85 @@ static void WalletShowInfo(CWallet* wallet_instance) tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size()); } -bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) +bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command, const std::string& name) { - fs::path path = fs::absolute(name, GetWalletDir()); + const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name); + + if (args.IsArgSet("-format") && command != "createfromdump") { + tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n"); + return false; + } + if (args.IsArgSet("-dumpfile") && command != "dump" && command != "createfromdump") { + tfm::format(std::cerr, "The -dumpfile option can only be used with the \"dump\" and \"createfromdump\" commands.\n"); + return false; + } + if (args.IsArgSet("-descriptors") && command != "create") { + tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n"); + return false; + } if (command == "create") { - std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true); + DatabaseOptions options; + options.require_create = true; + if (args.GetBoolArg("-descriptors", false)) { + options.create_flags |= WALLET_FLAG_DESCRIPTORS; + options.require_format = DatabaseFormat::SQLITE; + } + + std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options); if (wallet_instance) { WalletShowInfo(wallet_instance.get()); wallet_instance->Close(); } - } else if (command == "info" || command == "salvage") { - if (command == "info") { - 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") { + } else if (command == "info") { + DatabaseOptions options; + options.require_existing = true; + std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options); + if (!wallet_instance) return false; + WalletShowInfo(wallet_instance.get()); + wallet_instance->Close(); + } else if (command == "salvage") { #ifdef USE_BDB - 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); - } + 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); } - return ret; + if (!error.empty()) { + tfm::format(std::cerr, "%s\n", error.original); + } + } + return ret; #else - tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled"); - return false; + tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled"); + return false; #endif + } else if (command == "dump") { + DatabaseOptions options; + options.require_existing = true; + std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options); + if (!wallet_instance) return false; + bilingual_str error; + bool ret = DumpWallet(*wallet_instance, error); + if (!ret && !error.empty()) { + tfm::format(std::cerr, "%s\n", error.original); + return ret; + } + tfm::format(std::cout, "The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n"); + return ret; + } else if (command == "createfromdump") { + bilingual_str error; + std::vector<bilingual_str> warnings; + bool ret = CreateFromDump(name, path, error, warnings); + for (const auto& warning : warnings) { + tfm::format(std::cout, "%s\n", warning.original); + } + if (!ret && !error.empty()) { + tfm::format(std::cerr, "%s\n", error.original); } + return ret; } else { tfm::format(std::cerr, "Invalid command: %s\n", command); return false; diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h index d0b8d6812a..f544a6f727 100644 --- a/src/wallet/wallettool.h +++ b/src/wallet/wallettool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019 The Bitcoin Core developers +// 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. @@ -10,7 +10,7 @@ namespace WalletTool { void WalletShowInfo(CWallet* wallet_instance); -bool ExecuteWalletToolFunc(const std::string& command, const std::string& file); +bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command, const std::string& file); } // namespace WalletTool diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index d3f76ec66c..dd2f071b6c 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,17 +7,6 @@ #include <logging.h> #include <util/system.h> -#ifdef USE_BDB -bool ExistsBerkeleyDatabase(const fs::path& path); -#else -# define ExistsBerkeleyDatabase(path) (false) -#endif -#ifdef USE_SQLITE -bool ExistsSQLiteDatabase(const fs::path& path); -#else -# define ExistsSQLiteDatabase(path) (false) -#endif - fs::path GetWalletDir() { fs::path path; @@ -40,50 +29,6 @@ fs::path GetWalletDir() return path; } -std::vector<fs::path> ListWalletDir() -{ - const fs::path wallet_dir = GetWalletDir(); - const size_t offset = wallet_dir.string().size() + 1; - std::vector<fs::path> paths; - boost::system::error_code ec; - - for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) { - if (ec) { - LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string()); - continue; - } - - try { - // Get wallet path relative to walletdir by removing walletdir from the wallet path. - // 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 && - (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 && ExistsBerkeleyDatabase(it->path())) { - if (it->path().filename() == "wallet.dat") { - // Found top-level wallet.dat btree file, add top level directory "" - // as a wallet. - paths.emplace_back(); - } else { - // Found top-level btree file not called wallet.dat. Current bitcoin - // software will never create these files but will allow them to be - // opened in a shared database environment for backwards compatibility. - // Add it to the list of available wallets. - paths.emplace_back(path); - } - } - } catch (const std::exception& e) { - LogPrintf("%s: Error scanning %s: %s\n", __func__, it->path().string(), e.what()); - it.no_push(); - } - } - - return paths; -} - bool IsFeatureSupported(int wallet_version, int feature_version) { return wallet_version >= feature_version; @@ -91,13 +36,9 @@ bool IsFeatureSupported(int wallet_version, int feature_version) WalletFeature GetClosestWalletFeature(int version) { - if (version >= FEATURE_LATEST) return FEATURE_LATEST; - if (version >= FEATURE_PRE_SPLIT_KEYPOOL) return FEATURE_PRE_SPLIT_KEYPOOL; - if (version >= FEATURE_NO_DEFAULT_KEY) return FEATURE_NO_DEFAULT_KEY; - if (version >= FEATURE_HD_SPLIT) return FEATURE_HD_SPLIT; - if (version >= FEATURE_HD) return FEATURE_HD; - if (version >= FEATURE_COMPRPUBKEY) return FEATURE_COMPRPUBKEY; - if (version >= FEATURE_WALLETCRYPT) return FEATURE_WALLETCRYPT; - if (version >= FEATURE_BASE) return FEATURE_BASE; + static constexpr std::array wallet_features{FEATURE_LATEST, FEATURE_PRE_SPLIT_KEYPOOL, FEATURE_NO_DEFAULT_KEY, FEATURE_HD_SPLIT, FEATURE_HD, FEATURE_COMPRPUBKEY, FEATURE_WALLETCRYPT, FEATURE_BASE}; + for (const WalletFeature& wf : wallet_features) { + if (version >= wf) return wf; + } return static_cast<WalletFeature>(0); } diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index 27521abd81..67b2ee2b98 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -65,9 +65,6 @@ enum WalletFlags : uint64_t { //! Get the path of the wallet directory. fs::path GetWalletDir(); -//! Get wallets in wallet directory. -std::vector<fs::path> ListWalletDir(); - /** Descriptor with some wallet metadata */ class WalletDescriptor { diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index a55e02f2dc..660b0eed5d 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019 The Bitcoin Core developers +// Copyright (c) 2017-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/warnings.cpp b/src/warnings.cpp index 1dec663a73..60388cc713 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/warnings.h b/src/warnings.h index e87b64a86d..c38edb4570 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp index 3938f6fd2c..90aefb0018 100644 --- a/src/zmq/zmqabstractnotifier.cpp +++ b/src/zmq/zmqabstractnotifier.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h index dddba8d6b6..6f0b202a18 100644 --- a/src/zmq/zmqabstractnotifier.h +++ b/src/zmq/zmqabstractnotifier.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2018 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index a2f994d7df..86f47d71f3 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index 788a383517..8f81bfd63f 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2019 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index c0207f9dd6..168ba841c8 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -17,6 +17,7 @@ #include <cstdarg> #include <cstddef> #include <map> +#include <optional> #include <string> #include <utility> @@ -227,50 +228,43 @@ bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &tr return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size()); } +// Helper function to send a 'sequence' topic message with the following structure: +// <32-byte hash> | <1-byte label> | <8-byte LE sequence> (optional) +static bool SendSequenceMsg(CZMQAbstractPublishNotifier& notifier, uint256 hash, char label, std::optional<uint64_t> sequence = {}) +{ + unsigned char data[sizeof(hash) + sizeof(label) + sizeof(uint64_t)]; + for (unsigned int i = 0; i < sizeof(hash); ++i) { + data[sizeof(hash) - 1 - i] = hash.begin()[i]; + } + data[sizeof(hash)] = label; + if (sequence) WriteLE64(data + sizeof(hash) + sizeof(label), *sequence); + return notifier.SendZmqMessage(MSG_SEQUENCE, data, sequence ? sizeof(data) : sizeof(hash) + sizeof(label)); +} -// 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)); + return SendSequenceMsg(*this, hash, /* Block (C)onnect */ 'C'); } 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)); + return SendSequenceMsg(*this, hash, /* Block (D)isconnect */ 'D'); } 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)); + return SendSequenceMsg(*this, hash, /* Mempool (A)cceptance */ 'A', mempool_sequence); } 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)); + return SendSequenceMsg(*this, hash, /* Mempool (R)emoval */ 'R', mempool_sequence); } diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h index f13ed6f537..c1d66bddb1 100644 --- a/src/zmq/zmqpublishnotifier.h +++ b/src/zmq/zmqpublishnotifier.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2018 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp index 1dd751b493..81859f924f 100644 --- a/src/zmq/zmqrpc.cpp +++ b/src/zmq/zmqrpc.cpp @@ -52,9 +52,9 @@ static RPCHelpMan getzmqnotifications() } const CRPCCommand commands[] = -{ // category name actor (function) argNames - // ----------------- ------------------------ ----------------------- ---------- - { "zmq", "getzmqnotifications", &getzmqnotifications, {} }, +{ // category actor (function) + // ----------------- ----------------------- + { "zmq", &getzmqnotifications, }, }; } // anonymous namespace |