diff options
Diffstat (limited to 'src')
285 files changed, 3285 insertions, 3411 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 67fd402603..4a080ef1fb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,9 +20,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 @@ -299,14 +298,13 @@ libbitcoin_server_a_SOURCES = \ index/blockfilterindex.cpp \ index/txindex.cpp \ init.cpp \ - interfaces/chain.cpp \ - interfaces/node.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,18 +357,16 @@ 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/bdb.cpp \ wallet/coincontrol.cpp \ wallet/context.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/feebumper.cpp \ wallet/fees.cpp \ + wallet/interfaces.cpp \ wallet/load.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ - wallet/salvage.cpp \ wallet/scriptpubkeyman.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ @@ -381,6 +377,9 @@ libbitcoin_wallet_a_SOURCES = \ if USE_SQLITE libbitcoin_wallet_a_SOURCES += wallet/sqlite.cpp endif +if USE_BDB +libbitcoin_wallet_a_SOURCES += wallet/bdb.cpp wallet/salvage.cpp +endif libbitcoin_wallet_tool_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_tool_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) 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.test.include b/src/Makefile.test.include index 9cc383c240..cbfe93df0a 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 \ @@ -292,15 +136,19 @@ BITCOIN_TESTS =\ if ENABLE_WALLET BITCOIN_TESTS += \ - wallet/test/db_tests.cpp \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/wallet_tests.cpp \ + wallet/test/walletdb_tests.cpp \ wallet/test/wallet_crypto_tests.cpp \ wallet/test/coinselector_tests.cpp \ wallet/test/init_tests.cpp \ wallet/test/ismine_tests.cpp \ wallet/test/scriptpubkeyman_tests.cpp +if USE_BDB +BITCOIN_TESTS += wallet/test/db_tests.cpp +endif + BITCOIN_TEST_SUITE += \ wallet/test/wallet_test_fixture.cpp \ wallet/test/wallet_test_fixture.h \ @@ -330,935 +178,103 @@ 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/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/multiplication_overflow.cpp \ + test/fuzz/net.cpp \ + test/fuzz/net_permissions.cpp \ + test/fuzz/netaddress.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 @@ -1305,6 +321,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/addrman.h b/src/addrman.h index 04dd30b375..9ac67b7af6 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -281,8 +281,18 @@ protected: //! Select several addresses at once. void GetAddr_(std::vector<CAddress> &vAddr, size_t max_addresses, size_t max_pct) EXCLUSIVE_LOCKS_REQUIRED(cs); - //! Mark an entry as currently-connected-to. - void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); + /** We have successfully connected to this peer. Calling this function + * updates the CAddress's nTime, which is used in our IsTerrible() + * decisions and gossiped to peers. Callers should be careful that updating + * this information doesn't leak topology information to network spies. + * + * net_processing calls this function when it *disconnects* from a peer to + * not leak information about currently connected peers. + * + * @param[in] addr The address of the peer we were connected to + * @param[in] nTime The time that we were last connected to this peer + */ + void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); //! Update an entry's service bits. void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs); @@ -704,7 +714,7 @@ public: return vAddr; } - //! Mark an entry as currently-connected-to. + //! Outer function for Connected_() void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) { LOCK(cs); diff --git a/src/attributes.h b/src/attributes.h index 45099bd8b8..995c24e13f 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -6,17 +6,14 @@ #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/base58.cpp b/src/base58.cpp index 0dc6044145..780846c6c5 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -35,7 +35,7 @@ static const int8_t mapBase58[256] = { -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, }; -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..60551a12ae 100644 --- a/src/base58.h +++ b/src/base58.h @@ -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/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/prevector.cpp b/src/bench/prevector.cpp index a2dbefa54a..73af244ce0 100644 --- a/src/bench/prevector.cpp +++ b/src/bench/prevector.cpp @@ -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/wallet_balance.cpp b/src/bench/wallet_balance.cpp index b3b73284d8..aa436ee3ea 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -24,15 +24,13 @@ 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); diff --git a/src/bitcoin-cli-res.rc b/src/bitcoin-cli-res.rc index 58f8f1e8a2..405a302261 100644 --- a/src/bitcoin-cli-res.rc +++ b/src/bitcoin-cli-res.rc @@ -1,8 +1,8 @@ #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_REVISION,CLIENT_VERSION_BUILD -#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#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 diff --git a/src/bitcoin-tx-res.rc b/src/bitcoin-tx-res.rc index 3e49b820bc..b545ce9dbe 100644 --- a/src/bitcoin-tx-res.rc +++ b/src/bitcoin-tx-res.rc @@ -1,8 +1,8 @@ #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_REVISION,CLIENT_VERSION_BUILD -#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#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 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-wallet-res.rc b/src/bitcoin-wallet-res.rc index e9fa2dbb40..59346ab8f6 100644 --- a/src/bitcoin-wallet-res.rc +++ b/src/bitcoin-wallet-res.rc @@ -1,8 +1,8 @@ #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_REVISION,CLIENT_VERSION_BUILD -#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#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 diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index b9c2fe2d34..68890fda2d 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -24,9 +24,11 @@ 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("-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("-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); @@ -42,16 +44,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; } diff --git a/src/bitcoind-res.rc b/src/bitcoind-res.rc index 3a64acd5d1..a98b50c899 100644 --- a/src/bitcoind-res.rc +++ b/src/bitcoind-res.rc @@ -1,8 +1,8 @@ #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_REVISION,CLIENT_VERSION_BUILD -#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#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 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/bloom.h b/src/bloom.h index 9307257852..24dc607cd9 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -94,7 +94,18 @@ public: * insert()'ed ... but may also return true for items that were not inserted. * * It needs around 1.8 bytes per element per factor 0.1 of false positive rate. - * (More accurately: 3/(log(256)*log(2)) * log(1/fpRate) * nElements bytes) + * For example, if we want 1000 elements, we'd need: + * - ~1800 bytes for a false positive rate of 0.1 + * - ~3600 bytes for a false positive rate of 0.01 + * - ~5400 bytes for a false positive rate of 0.001 + * + * If we make these simplifying assumptions: + * - logFpRate / log(0.5) doesn't get rounded or clamped in the nHashFuncs calculation + * - nElements is even, so that nEntriesPerGeneration == nElements / 2 + * + * Then we get a more accurate estimate for filter bytes: + * + * 3/(log(256)*log(2)) * log(1/fpRate) * nElements */ class CRollingBloomFilter { 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/clientversion.cpp b/src/clientversion.cpp index 993967a180..aaf041602b 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -30,8 +30,7 @@ const std::string CLIENT_NAME("Satoshi"); #define BUILD_DESC BUILD_GIT_TAG #define BUILD_SUFFIX "" #else - #define BUILD_DESC "v" STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) \ - "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) + #define BUILD_DESC "v" STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_BUILD) #ifdef BUILD_GIT_COMMIT #define BUILD_SUFFIX "-" BUILD_GIT_COMMIT #elif defined(GIT_COMMIT_ID) @@ -45,10 +44,7 @@ const std::string CLIENT_BUILD(BUILD_DESC BUILD_SUFFIX); static std::string FormatVersion(int nVersion) { - if (nVersion % 100 == 0) - return strprintf("%d.%d.%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100); - else - return strprintf("%d.%d.%d.%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, nVersion % 100); + return strprintf("%d.%d.%d", nVersion / 10000, (nVersion / 100) % 100, nVersion % 100); } std::string FormatFullVersion() diff --git a/src/clientversion.h b/src/clientversion.h index 363094b696..c925b8f084 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -10,7 +10,7 @@ #endif //HAVE_CONFIG_H // Check that required client information is defined -#if !defined(CLIENT_VERSION_MAJOR) || !defined(CLIENT_VERSION_MINOR) || !defined(CLIENT_VERSION_REVISION) || !defined(CLIENT_VERSION_BUILD) || !defined(CLIENT_VERSION_IS_RELEASE) || !defined(COPYRIGHT_YEAR) +#if !defined(CLIENT_VERSION_MAJOR) || !defined(CLIENT_VERSION_MINOR) || !defined(CLIENT_VERSION_BUILD) || !defined(CLIENT_VERSION_IS_RELEASE) || !defined(COPYRIGHT_YEAR) #error Client version information missing: version is not defined by bitcoin-config.h or in any other way #endif @@ -36,9 +36,8 @@ #include <vector> static const int CLIENT_VERSION = - 1000000 * CLIENT_VERSION_MAJOR - + 10000 * CLIENT_VERSION_MINOR - + 100 * CLIENT_VERSION_REVISION + 10000 * CLIENT_VERSION_MAJOR + + 100 * CLIENT_VERSION_MINOR + 1 * CLIENT_VERSION_BUILD; extern const std::string CLIENT_NAME; diff --git a/src/compat.h b/src/compat.h index 0be02cae03..5fa6589792 100644 --- a/src/compat.h +++ b/src/compat.h @@ -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/core_io.h b/src/core_io.h index 80ec80cd50..aaee9c445d 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -22,8 +22,8 @@ class UniValue; // 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); /** diff --git a/src/core_read.cpp b/src/core_read.cpp index 121e62457c..7687a86185 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -15,16 +15,15 @@ #include <version.h> #include <boost/algorithm/string/classification.hpp> -#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> #include <algorithm> #include <string> -CScript ParseScript(const std::string& s) -{ - CScript result; +namespace { +opcodetype ParseOpCode(const std::string& s) +{ static std::map<std::string, opcodetype> mapOpNames; if (mapOpNames.empty()) @@ -40,11 +39,23 @@ CScript ParseScript(const std::string& s) continue; mapOpNames[strName] = static_cast<opcodetype>(op); // Convenience: OP_ADD and just ADD are both recognized: - boost::algorithm::replace_first(strName, "OP_", ""); - mapOpNames[strName] = static_cast<opcodetype>(op); + if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_" + mapOpNames[strName.substr(3)] = static_cast<opcodetype>(op); + } } } + auto it = mapOpNames.find(s); + if (it == mapOpNames.end()) throw std::runtime_error("script parse error: unknown opcode"); + return it->second; +} + +} // namespace + +CScript ParseScript(const std::string& s) +{ + CScript result; + std::vector<std::string> words; boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on); @@ -82,14 +93,10 @@ CScript ParseScript(const std::string& s) std::vector<unsigned char> value(w->begin()+1, w->end()-1); result << value; } - else if (mapOpNames.count(*w)) - { - // opcode, e.g. OP_ADD or ADD: - result << mapOpNames[*w]; - } else { - throw std::runtime_error("script parse error"); + // opcode, e.g. OP_ADD or ADD: + result << ParseOpCode(*w); } } @@ -119,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 presense 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/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/hash.h b/src/hash.h index 6d876076ee..083ac12523 100644 --- a/src/hash.h +++ b/src/hash.h @@ -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/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/init.cpp b/src/init.cpp index 495d96f938..46dce6aa97 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -85,7 +85,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 +98,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 +110,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) { @@ -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. @@ -236,17 +233,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 +292,7 @@ void Shutdown(NodeContext& node) globalVerifyHandle.reset(); ECC_Stop(); node.mempool.reset(); + node.fee_estimator.reset(); node.chainman = nullptr; node.scheduler.reset(); @@ -914,7 +903,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 +917,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.")); @@ -1039,7 +1031,7 @@ bool AppInitParameterInteraction(const ArgsManager& args) // Trim requested connection counts, to fit into system limitations // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695 - nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS); + nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind); #ifdef USE_POLL int fd_max = nFD; #else @@ -1384,27 +1376,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 = std::make_unique<PeerManager>(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 +1476,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 +1516,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 @@ -1792,13 +1777,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); @@ -1940,8 +1918,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..36f76aeb4f 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -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/net.cpp b/src/net.cpp index cf987b6995..af2afa218e 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> @@ -72,6 +73,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 +115,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) = {}; @@ -585,6 +588,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) } stats.fInbound = IsInboundConn(); stats.m_manual_connection = IsManualConn(); + X(m_bip152_highbandwidth_to); + X(m_bip152_highbandwidth_from); X(nStartingHeight); { LOCK(cs_vSend); @@ -629,34 +634,21 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) } #undef X -/** - * Receive bytes from the buffer and deserialize them into messages. - * - * @param[in] pch A pointer to the raw data - * @param[in] nBytes Size of the data - * @param[out] complete Set True if at least one message has been - * deserialized and is ready to be processed - * @return True if the peer should stay connected, - * False if the peer should be disconnected from. - */ -bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete) +bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete) { complete = false; const auto time = GetTime<std::chrono::microseconds>(); LOCK(cs_vRecv); nLastRecv = std::chrono::duration_cast<std::chrono::seconds>(time).count(); - nRecvBytes += nBytes; - while (nBytes > 0) { + nRecvBytes += msg_bytes.size(); + while (msg_bytes.size() > 0) { // absorb network data - int handled = m_deserializer->Read(pch, nBytes); + int handled = m_deserializer->Read(msg_bytes); if (handled < 0) { // Serious header problem, disconnect from the peer. return false; } - pch += handled; - nBytes -= handled; - if (m_deserializer->Complete()) { // decompose a transport agnostic CNetMessage from the deserializer uint32_t out_err_raw_size{0}; @@ -686,13 +678,13 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete return true; } -int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes) +int V1TransportDeserializer::readHeader(Span<const uint8_t> msg_bytes) { // copy data to temporary parsing buffer unsigned int nRemaining = CMessageHeader::HEADER_SIZE - nHdrPos; - unsigned int nCopy = std::min(nRemaining, nBytes); + unsigned int nCopy = std::min<unsigned int>(nRemaining, msg_bytes.size()); - memcpy(&hdrbuf[nHdrPos], pch, nCopy); + memcpy(&hdrbuf[nHdrPos], msg_bytes.data(), nCopy); nHdrPos += nCopy; // if header incomplete, exit @@ -726,18 +718,18 @@ int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes) return nCopy; } -int V1TransportDeserializer::readData(const char *pch, unsigned int nBytes) +int V1TransportDeserializer::readData(Span<const uint8_t> msg_bytes) { unsigned int nRemaining = hdr.nMessageSize - nDataPos; - unsigned int nCopy = std::min(nRemaining, nBytes); + unsigned int nCopy = std::min<unsigned int>(nRemaining, msg_bytes.size()); if (vRecv.size() < nDataPos + nCopy) { // Allocate up to 256 KiB ahead, but never more than the total message size. vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024)); } - hasher.Write({(const unsigned char*)pch, nCopy}); - memcpy(&vRecv[nDataPos], pch, nCopy); + hasher.Write(msg_bytes.first(nCopy)); + memcpy(&vRecv[nDataPos], msg_bytes.data(), nCopy); nDataPos += nCopy; return nCopy; @@ -853,21 +845,6 @@ size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pno 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; @@ -923,43 +900,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. @@ -997,7 +939,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) @@ -1029,10 +971,52 @@ 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) { pnode->fDisconnect = true; return true; } @@ -1244,7 +1228,7 @@ void CConnman::InactivityCheck(CNode *pnode) 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)) + else if (nTime - pnode->nLastRecv > TIMEOUT_INTERVAL) { LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); pnode->fDisconnect = true; @@ -1476,18 +1460,18 @@ void CConnman::SocketHandler() if (recvSet || errorSet) { // typical socket buffer is 8K-64K - char pchBuf[0x10000]; + uint8_t pchBuf[0x10000]; int nBytes = 0; { LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) continue; - nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + nBytes = recv(pnode->hSocket, (char*)pchBuf, sizeof(pchBuf), MSG_DONTWAIT); } if (nBytes > 0) { bool notify = false; - if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) + if (!pnode->ReceiveMsgBytes(Span<const uint8_t>(pchBuf, nBytes), notify)) pnode->CloseSocketDisconnect(); RecordBytesRecv(nBytes); if (notify) { @@ -1839,18 +1823,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 nOutbound = 0; + int full_outbound_peers = 0; { LOCK(cs_vNodes); for (const CNode* pnode : vNodes) { - if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsOutboundOrBlockRelayConn()) { - ++nOutbound; + if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsFullOutboundConn()) { + ++full_outbound_peers; } } } - return std::max(nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0); + return std::max(full_outbound_peers - m_max_outbound_full_relay, 0); +} + +int CConnman::GetExtraBlockRelayCount() +{ + int block_relay_peers = 0; + { + LOCK(cs_vNodes); + for (const CNode* pnode : vNodes) { + if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsBlockOnlyConn()) { + ++block_relay_peers; + } + } + } + return std::max(block_relay_peers - m_max_outbound_block_relay, 0); } void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) @@ -1881,6 +1879,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(); @@ -1953,8 +1952,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; @@ -1965,6 +1965,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; @@ -2445,17 +2469,6 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) { Init(connOptions); - { - LOCK(cs_totalBytesRecv); - nTotalBytesRecv = 0; - } - { - LOCK(cs_totalBytesSent); - nTotalBytesSent = 0; - nMaxOutboundTotalBytesSentInCycle = 0; - nMaxOutboundCycleStartTime = 0; - } - if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds, connOptions.onion_binds)) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( @@ -2830,8 +2843,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; @@ -2842,48 +2855,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) @@ -2895,8 +2889,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; } @@ -2946,18 +2940,15 @@ 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) : 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), + nMyStartingHeight(nMyStartingHeightIn), + m_inbound_onion(inbound_onion) { hSocket = hSocketIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; @@ -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). */ @@ -77,9 +76,7 @@ static const bool DEFAULT_UPNP = false; /** 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 */ @@ -209,7 +206,6 @@ public: 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; @@ -241,7 +237,6 @@ public: m_peer_connect_timeout = connOptions.m_peer_connect_timeout; { LOCK(cs_totalBytesSent); - nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; } vWhitelistedRange = connOptions.vWhitelistedRange; @@ -334,13 +329,20 @@ public: 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 GetExtraOutboundCount(); + 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); @@ -361,13 +363,8 @@ public: //! 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(); + std::chrono::seconds GetMaxOutboundTimeframe(); //! check if the outbound target is reached //! if param historicalBlockServingLimit is set true, the function will @@ -378,9 +375,9 @@ public: //! 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(); + //! 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(); @@ -478,10 +475,9 @@ private: uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent) {0}; // outbound limit & stats - uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent); - uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent) {0}; + std::chrono::seconds 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; @@ -598,6 +594,12 @@ private: * 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}; /** @@ -669,7 +671,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; @@ -702,6 +703,8 @@ public: std::string cleanSubVer; bool fInbound; bool m_manual_connection; + bool m_bip152_highbandwidth_to; + bool m_bip152_highbandwidth_from; int nStartingHeight; uint64_t nSendBytes; mapMsgCmdSize mapSendBytesPerMsgCmd; @@ -757,8 +760,8 @@ public: virtual bool Complete() const = 0; // set the serialization context version virtual void SetVersion(int version) = 0; - // read and deserialize data - virtual int Read(const char *data, unsigned int bytes) = 0; + /** read and deserialize data, advances msg_bytes data pointer */ + virtual int Read(Span<const uint8_t>& msg_bytes) = 0; // decomposes a message from the context virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0; virtual ~TransportDeserializer() {} @@ -779,8 +782,8 @@ private: unsigned int nDataPos; const uint256& GetMessageHash() const; - int readHeader(const char *pch, unsigned int nBytes); - int readData(const char *pch, unsigned int nBytes); + int readHeader(Span<const uint8_t> msg_bytes); + int readData(Span<const uint8_t> msg_bytes); void Reset() { vRecv.clear(); @@ -814,9 +817,14 @@ public: hdrbuf.SetVersion(nVersionIn); vRecv.SetVersion(nVersionIn); } - int Read(const char *pch, unsigned int nBytes) override { - int ret = in_data ? readData(pch, nBytes) : readHeader(pch, nBytes); - if (ret < 0) Reset(); + int Read(Span<const uint8_t>& msg_bytes) override + { + int ret = in_data ? readData(msg_bytes) : readHeader(msg_bytes); + if (ret < 0) { + Reset(); + } else { + msg_bytes = msg_bytes.subspan(ret); + } return ret; } Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override; @@ -947,6 +955,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; } @@ -984,6 +995,10 @@ protected: 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; @@ -1015,7 +1030,7 @@ 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; @@ -1048,7 +1063,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. @@ -1118,10 +1133,20 @@ public: return nRefCount; } - bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete); + /** + * Receive bytes from the buffer and deserialize them into messages. + * + * @param[in] msg_bytes The raw data + * @param[out] complete Set True if at least one message has been + * deserialized and is ready to be processed + * @return True if the peer should stay connected, + * False if the peer should be disconnected from. + */ + bool ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete); void SetCommonVersion(int greatest_common_version) { + Assume(m_greatest_common_version == INIT_PROTO_VERSION); m_greatest_common_version = greatest_common_version; } int GetCommonVersion() const @@ -1152,18 +1177,23 @@ public: 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 { @@ -1215,4 +1245,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_processing.cpp b/src/net_processing.cpp index c649cf7757..a33c4a0bd4 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -290,8 +290,6 @@ namespace { struct CNodeState { //! The peer's address const CService address; - //! Whether we have a fully established connection. - bool fCurrentlyConnected; //! The best known block we know this peer has announced. const CBlockIndex *pindexBestKnownBlock; //! The hash of the last unknown block this peer has announced. @@ -378,19 +376,15 @@ 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) { - fCurrentlyConnected = false; pindexBestKnownBlock = nullptr; hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = nullptr; @@ -425,58 +419,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; @@ -487,32 +429,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) { @@ -638,11 +554,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; }); @@ -763,6 +683,32 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec } // namespace +void PeerManager::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(); + 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); + + m_connman.PushMessage(&pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, + nonce, strSubVersion, nNodeStartingHeight, !m_ignore_incoming_txs && 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); + } +} + void PeerManager::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) { AssertLockHeld(::cs_main); // For m_txrequest @@ -805,16 +751,16 @@ void PeerManager::InitializeNode(CNode *pnode) { 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()); } } @@ -845,11 +791,9 @@ void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) { LOCK(cs_main); int misbehavior{0}; { - PeerRef peer = GetPeerRef(nodeid); + 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); @@ -857,8 +801,9 @@ void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) { if (state->fSyncStarted) nSyncStarted--; - if (misbehavior == 0 && state->fCurrentlyConnected && !node.IsBlockOnlyConn()) { - // Note: we avoid changing visible addrman state for block-relay-only peers + if (node.fSuccessfullyConnected && misbehavior == 0 && + !node.IsBlockOnlyConn() && !node.IsInboundConn()) { + // Only change visible addrman state for outbound, full-relay peers fUpdateConnectionTime = true; } @@ -889,7 +834,26 @@ 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 PeerManager::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 PeerManager::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 PeerManager::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { { LOCK(cs_main); CNodeState* state = State(nodeid); @@ -1087,8 +1051,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; } @@ -1161,13 +1125,15 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para } PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, - CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool) + 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)); @@ -1324,34 +1290,33 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ * 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); - + m_connman.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); - } - } - }); - m_connman.WakeMessageHandler(); } + + // Relay to all peers + m_connman.ForEachNode([&vHashes](CNode* pnode) { + LOCK(pnode->cs_inventory); + for (const uint256& hash : reverse_iterate(vHashes)) { + pnode->vBlockHashesToAnnounce.push_back(hash); + } + }); + m_connman.WakeMessageHandler(); } /** @@ -1445,7 +1410,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; @@ -1462,8 +1443,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) { @@ -2286,10 +2267,8 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat 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; } @@ -2349,9 +2328,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); @@ -2364,10 +2343,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); @@ -2477,7 +2462,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; } @@ -2488,13 +2473,10 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat if (pfrom.fSuccessfullyConnected) return; if (!pfrom.IsInboundConn()) { - // Mark this node as currently connected, so we update its timestamp later. - LOCK(cs_main); - State(pfrom.GetId())->fCurrentlyConnected = true; LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n", pfrom.nVersion.load(), pfrom.nStartingHeight, pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) : ""), - pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay"); + pfrom.IsBlockOnlyConn() ? "block-relay" : "full-relay"); } if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) { @@ -2521,8 +2503,41 @@ 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 @@ -2540,6 +2555,17 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat 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. + 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; @@ -2593,7 +2619,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) @@ -2607,40 +2633,6 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat 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; - } - if (msg_type == NetMsgType::INV) { std::vector<CInv> vInv; vRecv >> vInv; @@ -2652,7 +2644,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)) { @@ -2929,7 +2921,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; @@ -3791,7 +3783,7 @@ 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; @@ -3927,11 +3919,54 @@ void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) void PeerManager::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; @@ -3940,14 +3975,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; @@ -4003,6 +4037,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 { @@ -4079,6 +4118,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); } diff --git a/src/net_processing.h b/src/net_processing.h index 87eee566de..12a4e9c38f 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -32,10 +32,52 @@ 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}; +struct CNodeStateStats { + int m_misbehavior_score = 0; + int nSyncHeight = -1; + int nCommonHeight = -1; + std::vector<int> vHeightInFlight; +}; + +/** + * 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); + + explicit Peer(NodeId id) : m_id(id) {} +}; + +using PeerRef = std::shared_ptr<Peer>; + class PeerManager final : public CValidationInterface, public NetEventsInterface { public: PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, - CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool); + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, + bool ignore_incoming_txs); /** * Overridden from CValidationInterface. @@ -94,7 +136,21 @@ public: */ void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message); + /** Get statistics from node state */ + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats); + + /** Whether this node ignores txs received over p2p. */ + bool IgnoresIncomingTxs() {return m_ignore_incoming_txs;}; + private: + /** 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 * @@ -134,6 +190,9 @@ private: 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. */ @@ -143,17 +202,24 @@ private: 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; -}; + //* 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}; -/** Get statistics from node state */ -bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); + /** Protects m_peer_map */ + 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); +}; /** 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..b1f9d32d34 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; diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp index fb46ea1731..02e50c4dbe 100644 --- a/src/node/coinstats.cpp +++ b/src/node/coinstats.cpp @@ -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/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..317a5c7cbe 100644 --- a/src/interfaces/chain.cpp +++ b/src/node/interfaces.cpp @@ -2,18 +2,25 @@ // 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 <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 +30,282 @@ #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) 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(); -bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock) + 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, const CChain& active) { if (!index) return false; if (block.m_hash) *block.m_hash = index->GetBlockHash(); @@ -46,6 +313,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 +417,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 +453,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 = FindForkInGlobalIndex(active, locator)) { return fork->nHeight; } return nullopt; @@ -207,47 +462,45 @@ 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(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); + const CChain& active = Assert(m_node.chainman)->ActiveChain(); if (const CBlockIndex* block = 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 CChain& active = Assert(m_node.chainman)->ActiveChain(); const CBlockIndex* block = LookupBlockIndex(block_hash); const CBlockIndex* ancestor = 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 CChain& active = Assert(m_node.chainman)->ActiveChain(); const CBlockIndex* block1 = LookupBlockIndex(block_hash1); const CBlockIndex* block2 = 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 @@ -327,11 +580,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 +620,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 +667,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.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/outputtype.h b/src/outputtype.h index 77a16b1d05..bb7f39323b 100644 --- a/src/outputtype.h +++ b/src/outputtype.h @@ -22,7 +22,7 @@ enum class OutputType { extern const std::array<OutputType, 3> OUTPUT_TYPES; -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/fees.cpp b/src/policy/fees.cpp index 0f31093dbb..cfa4cf8421 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -6,10 +6,14 @@ #include <policy/fees.h> #include <clientversion.h> +#include <fs.h> +#include <logging.h> #include <streams.h> #include <txmempool.h> #include <util/system.h> +static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; + static constexpr double INF_FEERATE = 1e99; std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) { @@ -489,6 +493,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 +505,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() @@ -856,6 +868,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 +909,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 +929,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..dd9f530c99 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -215,6 +215,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..8e367d31d0 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -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..fc163e958b 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -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/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..ec09668e7a 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -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..d7b73dfa40 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -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..8af34f58bd 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -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.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.h b/src/pubkey.h index 0f784b86e4..d60520ac44 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -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/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 3d1963b6e6..3be8b664dd 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -58,14 +58,6 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri ui->passEdit3->hide(); setWindowTitle(tr("Unlock wallet")); break; - case Decrypt: // Ask passphrase - ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet.")); - ui->passLabel2->hide(); - ui->passEdit2->hide(); - ui->passLabel3->hide(); - ui->passEdit3->hide(); - setWindowTitle(tr("Decrypt wallet")); - break; case ChangePass: // Ask old passphrase + new passphrase x2 setWindowTitle(tr("Change passphrase")); ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet.")); @@ -133,8 +125,7 @@ void AskPassphraseDialog::accept() "</b></qt>"); } else { assert(model != nullptr); - if(model->setWalletEncrypted(true, newpass1)) - { + if (model->setWalletEncrypted(newpass1)) { QMessageBox::warning(this, tr("Wallet encrypted"), "<qt>" + tr("Your wallet is now encrypted. ") + encryption_reminder + @@ -144,9 +135,7 @@ void AskPassphraseDialog::accept() "For security reasons, previous backups of the unencrypted wallet file " "will become useless as soon as you start using the new, encrypted wallet.") + "</b></qt>"); - } - else - { + } else { QMessageBox::critical(this, tr("Wallet encryption failed"), tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted.")); } @@ -176,17 +165,6 @@ void AskPassphraseDialog::accept() QMessageBox::critical(this, tr("Wallet unlock failed"), e.what()); } break; - case Decrypt: - if(!model->setWalletEncrypted(false, oldpass)) - { - QMessageBox::critical(this, tr("Wallet decryption failed"), - tr("The passphrase entered for the wallet decryption was incorrect.")); - } - else - { - QDialog::accept(); // Success - } - break; case ChangePass: if(newpass1 == newpass2) { @@ -221,7 +199,6 @@ void AskPassphraseDialog::textChanged() acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; case Unlock: // Old passphrase x1 - case Decrypt: acceptable = !ui->passEdit1->text().isEmpty(); break; case ChangePass: // Old passphrase x1, new passphrase x2 diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 9557e72936..f3ba882277 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -26,7 +26,6 @@ public: Encrypt, /**< Ask passphrase twice and encrypt */ Unlock, /**< Ask passphrase and unlock */ ChangePass, /**< Ask old passphrase + new passphrase twice */ - Decrypt /**< Ask passphrase and decrypt wallet */ }; explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr); diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 2739b21a9d..2676de96d7 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> @@ -122,7 +123,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 63b4107f7e..4e1b239bc7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -263,7 +263,7 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { assert(!m_splash); - m_splash = new SplashScreen(nullptr, networkStyle); + m_splash = new SplashScreen(networkStyle); // We don't hold a direct pointer to the splash screen after creation, but the splash // screen will take care of deleting itself when finish() happens. m_splash->show(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 23370e6ad3..f2a49e5a76 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -615,10 +615,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 @@ -1311,7 +1311,7 @@ void BitcoinGUI::updateProxyIcon() bool proxy_enabled = clientModel->getProxyInfo(ip_port); if (proxy_enabled) { - if (labelProxyIcon->pixmap() == nullptr) { + if (!GUIUtil::HasPixmap(labelProxyIcon)) { QString ip_port_q = QString::fromStdString(ip_port); labelProxyIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/proxy").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q)); @@ -1387,14 +1387,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 912297a74e..147f19e68d 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -49,6 +49,7 @@ struct BlockAndHeaderTipInfo; QT_BEGIN_NAMESPACE class QAction; class QComboBox; +class QDateTime; class QMenu; class QProgressBar; class QProgressDialog; @@ -317,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/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp index 2ded6a1d89..3945159c26 100644 --- a/src/qt/createwalletdialog.cpp +++ b/src/qt/createwalletdialog.cpp @@ -51,12 +51,15 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) : } }); - #ifndef USE_SQLITE +#ifndef USE_SQLITE ui->descriptor_checkbox->setToolTip(tr("Compiled without sqlite support (required for descriptor wallets)")); ui->descriptor_checkbox->setEnabled(false); ui->descriptor_checkbox->setChecked(false); - #endif - +#endif +#ifndef USE_BDB + ui->descriptor_checkbox->setEnabled(false); + ui->descriptor_checkbox->setChecked(true); +#endif } CreateWalletDialog::~CreateWalletDialog() diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index d210faec03..139c8e161e 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -109,39 +109,13 @@ </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="label_berkeleyDBVersion"> - <property name="text"> - <string>Using BerkeleyDB version</string> - </property> - <property name="indent"> - <number>10</number> - </property> - </widget> - </item> - <item row="3" column="1" colspan="2"> - <widget class="QLabel" name="berkeleyDBVersion"> - <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="4" column="0"> <widget class="QLabel" name="label_12"> <property name="text"> <string>Datadir</string> </property> </widget> </item> - <item row="4" column="1" colspan="2"> + <item row="3" column="1" colspan="2"> <widget class="QLabel" name="dataDir"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -163,14 +137,14 @@ </property> </widget> </item> - <item row="5" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="label_11"> <property name="text"> <string>Blocksdir</string> </property> </widget> </item> - <item row="5" column="1" colspan="2"> + <item row="4" column="1" colspan="2"> <widget class="QLabel" name="blocksDir"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -192,14 +166,14 @@ </property> </widget> </item> - <item row="6" column="0"> + <item row="5" column="0"> <widget class="QLabel" name="label_13"> <property name="text"> <string>Startup time</string> </property> </widget> </item> - <item row="6" column="1" colspan="2"> + <item row="5" column="1" colspan="2"> <widget class="QLabel" name="startupTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -215,7 +189,7 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="labelNetwork"> <property name="font"> <font> @@ -228,14 +202,14 @@ </property> </widget> </item> - <item row="8" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> <string>Name</string> </property> </widget> </item> - <item row="8" column="1" colspan="2"> + <item row="7" column="1" colspan="2"> <widget class="QLabel" name="networkName"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -251,14 +225,14 @@ </property> </widget> </item> - <item row="9" column="0"> + <item row="8" column="0"> <widget class="QLabel" name="label_7"> <property name="text"> <string>Number of connections</string> </property> </widget> </item> - <item row="9" column="1" colspan="2"> + <item row="8" column="1" colspan="2"> <widget class="QLabel" name="numberOfConnections"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -274,7 +248,7 @@ </property> </widget> </item> - <item row="10" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_10"> <property name="font"> <font> @@ -287,14 +261,14 @@ </property> </widget> </item> - <item row="11" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Current block height</string> </property> </widget> </item> - <item row="11" column="1" colspan="2"> + <item row="10" column="1" colspan="2"> <widget class="QLabel" name="numberOfBlocks"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -310,14 +284,14 @@ </property> </widget> </item> - <item row="12" column="0"> + <item row="11" column="0"> <widget class="QLabel" name="labelLastBlockTime"> <property name="text"> <string>Last block time</string> </property> </widget> </item> - <item row="12" column="1" colspan="2"> + <item row="11" column="1" colspan="2"> <widget class="QLabel" name="lastBlockTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -333,7 +307,7 @@ </property> </widget> </item> - <item row="13" column="0"> + <item row="12" column="0"> <widget class="QLabel" name="labelMempoolTitle"> <property name="font"> <font> @@ -346,14 +320,14 @@ </property> </widget> </item> - <item row="14" column="0"> + <item row="13" column="0"> <widget class="QLabel" name="labelNumberOfTransactions"> <property name="text"> <string>Current number of transactions</string> </property> </widget> </item> - <item row="14" column="1"> + <item row="13" column="1"> <widget class="QLabel" name="mempoolNumberTxs"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -369,14 +343,14 @@ </property> </widget> </item> - <item row="15" column="0"> + <item row="14" column="0"> <widget class="QLabel" name="labelMemoryUsage"> <property name="text"> <string>Memory usage</string> </property> </widget> </item> - <item row="15" column="1"> + <item row="14" column="1"> <widget class="QLabel" name="mempoolSize"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -392,7 +366,7 @@ </property> </widget> </item> - <item row="13" column="2" rowspan="3"> + <item row="12" column="2" rowspan="3"> <layout class="QVBoxLayout" name="verticalLayoutDebugButton"> <property name="spacing"> <number>3</number> @@ -432,7 +406,7 @@ </item> </layout> </item> - <item row="16" column="0"> + <item row="15" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 0016fb9739..88944a58a6 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -55,7 +55,7 @@ <item> <widget class="QCheckBox" name="prune"> <property name="toolTip"> - <string>Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.</string> + <string>Enabling pruning significantly reduces the disk space required to store transactions. All blocks are still fully validated. Reverting this setting requires re-downloading the entire blockchain.</string> </property> <property name="text"> <string>Prune &block storage to</string> @@ -568,12 +568,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/guiutil.cpp b/src/qt/guiutil.cpp index bab17562a6..53ffb27f50 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -43,6 +43,7 @@ #include <QKeyEvent> #include <QLineEdit> #include <QList> +#include <QLocale> #include <QMenu> #include <QMouseEvent> #include <QProgressDialog> @@ -67,7 +68,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) @@ -920,4 +921,35 @@ void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action) menu->popup(point, at_action); } +QDateTime StartOfDay(const QDate& date) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + return date.startOfDay(); +#else + return QDateTime(date); +#endif +} + +bool HasPixmap(const QLabel* label) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + return !label->pixmap(Qt::ReturnByValue).isNull(); +#else + return label->pixmap() != nullptr; +#endif +} + +QImage GetImage(const QLabel* label) +{ + if (!HasPixmap(label)) { + return QImage(); + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + return label->pixmap(Qt::ReturnByValue).toImage(); +#else + return label->pixmap()->toImage(); +#endif +} + } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 2bd94b5eb3..d7bd124884 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -289,7 +289,7 @@ namespace GUIUtil /** * Returns the distance in pixels appropriate for drawing a subsequent character after text. * - * In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 13.0. + * In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 5.13. * In Qt 5.11 the QFontMetrics::horizontalAdvance() was introduced. */ int TextWidth(const QFontMetrics& fm, const QString& text); @@ -303,6 +303,56 @@ namespace GUIUtil * Call QMenu::popup() only on supported QT_QPA_PLATFORM. */ void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action = nullptr); + + /** + * Returns the start-moment of the day in local time. + * + * QDateTime::QDateTime(const QDate& date) is deprecated since Qt 5.15. + * QDate::startOfDay() was introduced in Qt 5.14. + */ + QDateTime StartOfDay(const QDate& date); + + /** + * Returns true if pixmap has been set. + * + * QPixmap* QLabel::pixmap() is deprecated since Qt 5.15. + */ + bool HasPixmap(const QLabel* label); + QImage GetImage(const QLabel* label); + + /** + * Splits the string into substrings wherever separator occurs, and returns + * the list of those strings. Empty strings do not appear in the result. + * + * QString::split() signature differs in different Qt versions: + * - QString::SplitBehavior is deprecated since Qt 5.15 + * - Qt::SplitBehavior was introduced in Qt 5.14 + * If {QString|Qt}::SkipEmptyParts behavior is required, use this + * function instead of QString::split(). + */ + template <typename SeparatorType> + QStringList SplitSkipEmptyParts(const QString& string, const SeparatorType& separator) + { + #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + return string.split(separator, Qt::SkipEmptyParts); + #else + return string.split(separator, QString::SkipEmptyParts); + #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 #endif // BITCOIN_QT_GUIUTIL_H diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 4b91c19761..d518a2065c 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -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 diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index ae6aeb7709..78d4dd5557 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -130,8 +130,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); } @@ -227,7 +227,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 +286,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..3a7b9192a1 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -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 7e089b4f95..152de6decb 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -54,14 +54,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); @@ -219,7 +220,7 @@ static ProxySetting GetProxySetting(QSettings &settings, const QString &name) return default_val; } // contains IP at index 0 and port at index 1 - QStringList ip_port = settings.value(name).toString().split(":", QString::SkipEmptyParts); + QStringList ip_port = GUIUtil::SplitSkipEmptyParts(settings.value(name).toString(), ":"); if (ip_port.size() == 2) { return {true, ip_port.at(0), ip_port.at(1)}; } else { // Invalid: return default @@ -272,8 +273,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: @@ -342,10 +343,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(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 3d9e7bbb80..f1929a5e06 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -45,7 +45,7 @@ public: enum OptionID { StartAtStartup, // bool - HideTrayIcon, // bool + ShowTrayIcon, // bool MinimizeToTray, // bool MapPortUPnP, // bool MinimizeOnClose, // bool @@ -78,7 +78,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 +100,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 +118,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 b536567c8b..1297eb8b75 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -17,6 +17,7 @@ #include <QAbstractItemDelegate> #include <QApplication> +#include <QDateTime> #include <QPainter> #include <QStatusTipEvent> diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 6c2db52f63..86d681aa6f 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -26,7 +26,6 @@ #include <QApplication> #include <QByteArray> #include <QDataStream> -#include <QDateTime> #include <QDebug> #include <QFile> #include <QFileOpenEvent> diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp index 52f1e60957..141f4abb3b 100644 --- a/src/qt/qrimagewidget.cpp +++ b/src/qt/qrimagewidget.cpp @@ -98,15 +98,12 @@ bool QRImageWidget::setQR(const QString& data, const QString& text) QImage QRImageWidget::exportImage() { - if(!pixmap()) - return QImage(); - return pixmap()->toImage(); + return GUIUtil::GetImage(this); } void QRImageWidget::mousePressEvent(QMouseEvent *event) { - if(event->button() == Qt::LeftButton && pixmap()) - { + if (event->button() == Qt::LeftButton && GUIUtil::HasPixmap(this)) { event->accept(); QMimeData *mimeData = new QMimeData; mimeData->setImageData(exportImage()); @@ -121,7 +118,7 @@ void QRImageWidget::mousePressEvent(QMouseEvent *event) void QRImageWidget::saveImage() { - if(!pixmap()) + if (!GUIUtil::HasPixmap(this)) return; QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr); if (!fn.isEmpty()) @@ -132,14 +129,14 @@ void QRImageWidget::saveImage() void QRImageWidget::copyImage() { - if(!pixmap()) + if (!GUIUtil::HasPixmap(this)) return; QApplication::clipboard()->setImage(exportImage()); } void QRImageWidget::contextMenuEvent(QContextMenuEvent *event) { - if(!pixmap()) + if (!GUIUtil::HasPixmap(this)) return; contextMenu->exec(event->globalPos()); } diff --git a/src/qt/res/bitcoin-qt-res.rc b/src/qt/res/bitcoin-qt-res.rc index 94ae256477..e590b407b0 100644 --- a/src/qt/res/bitcoin-qt-res.rc +++ b/src/qt/res/bitcoin-qt-res.rc @@ -4,8 +4,8 @@ IDI_ICON2 ICON DISCARDABLE "icons/bitcoin_testnet.ico" #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_REVISION,CLIENT_VERSION_BUILD -#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#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 diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 4c5601242e..236c6e13d5 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -25,10 +25,14 @@ #include <univalue.h> #ifdef ENABLE_WALLET +#ifdef USE_BDB +#include <wallet/bdb.h> +#endif #include <wallet/db.h> #include <wallet/wallet.h> #endif +#include <QDateTime> #include <QFont> #include <QKeyEvent> #include <QMenu> @@ -479,13 +483,6 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty ui->WalletSelector->setVisible(false); ui->WalletSelectorLabel->setVisible(false); - // set library version labels -#ifdef ENABLE_WALLET - ui->berkeleyDBVersion->setText(QString::fromStdString(BerkeleyDatabaseVersion())); -#else - ui->label_berkeleyDBVersion->hide(); - ui->berkeleyDBVersion->hide(); -#endif // Register RPC timer interface rpcTimerInterface = new QtRPCTimerInterface(); // avoid accidentally overwriting an existing, non QTThread @@ -493,9 +490,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); - - ui->detailWidget->hide(); - ui->peerHeading->setText(tr("Select a peer to view detailed information.")); + updateDetailWidget(); consoleFontSize = settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt(); clear(); @@ -626,7 +621,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ connect(disconnectAction, &QAction::triggered, this, &RPCConsole::disconnectSelectedNode); // peer table signal handling - update peer details when selecting new node - connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::peerSelected); + connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget); // peer table signal handling - update peer details when new nodes are added to the model connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::peerLayoutChanged); // peer table signal handling - cache selected node ids @@ -1021,18 +1016,6 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut)); } -void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected) -{ - Q_UNUSED(deselected); - - if (!clientModel || !clientModel->getPeerTableModel() || selected.indexes().isEmpty()) - return; - - const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row()); - if (stats) - updateNodeDetail(stats); -} - void RPCConsole::peerLayoutAboutToChange() { QModelIndexList selected = ui->peerWidget->selectionModel()->selectedIndexes(); @@ -1049,7 +1032,6 @@ void RPCConsole::peerLayoutChanged() if (!clientModel || !clientModel->getPeerTableModel()) return; - const CNodeCombinedStats *stats = nullptr; bool fUnselect = false; bool fReselect = false; @@ -1080,9 +1062,6 @@ void RPCConsole::peerLayoutChanged() fUnselect = true; fReselect = true; } - - // get fresh stats on the detail node. - stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); } if (fUnselect && selectedRow >= 0) { @@ -1097,12 +1076,20 @@ void RPCConsole::peerLayoutChanged() } } - if (stats) - updateNodeDetail(stats); + updateDetailWidget(); } -void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) +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) { + 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()); // 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)); @@ -1260,8 +1247,7 @@ void RPCConsole::clearSelectedNode() { ui->peerWidget->selectionModel()->clearSelection(); cachedNodeids.clear(); - ui->detailWidget->hide(); - ui->peerHeading->setText(tr("Select a peer to view detailed information.")); + updateDetailWidget(); } void RPCConsole::showOrHideBanTableIfRequired() diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 280c5bd71a..8fea08ab5c 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -28,6 +28,7 @@ namespace Ui { } QT_BEGIN_NAMESPACE +class QDateTime; class QMenu; class QItemSelection; QT_END_NAMESPACE @@ -94,6 +95,8 @@ private Q_SLOTS: void showOrHideBanTableIfRequired(); /** clear the selected node */ void clearSelectedNode(); + /** show detailed information on ui about selected node */ + void updateDetailWidget(); public Q_SLOTS: void clear(bool clearHistory = true); @@ -115,8 +118,6 @@ public Q_SLOTS: void browseHistory(int offset); /** Scroll console view to end */ void scrollToEnd(); - /** Handle selection of peer in peers list */ - void peerSelected(const QItemSelection &selected, const QItemSelection &deselected); /** Handle selection caching before update */ void peerLayoutAboutToChange(); /** Handle updated peer information */ @@ -137,8 +138,6 @@ Q_SIGNALS: private: void startExecutor(); void setTrafficGraphRange(int mins); - /** show detailed information on ui about selected node */ - void updateNodeDetail(const CNodeCombinedStats *stats); enum ColumnWidths { diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 50a1ea6936..e765e643a3 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(); @@ -173,8 +173,15 @@ void SendCoinsDialog::setModel(WalletModel *_model) } connect(ui->confTargetSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel); connect(ui->confTargetSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::updateFeeSectionControls); + connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::coinControlUpdateLabels); +#else connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls); connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels); +#endif + connect(ui->customFee, &BitcoinAmountField::valueChanged, this, &SendCoinsDialog::coinControlUpdateLabels); connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel); connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index f00f086d1e..5796a6ef56 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -25,8 +25,8 @@ #include <QScreen> -SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) : - QWidget(nullptr, f), curAlignment(0) +SplashScreen::SplashScreen(const NetworkStyle* networkStyle) + : QWidget(), curAlignment(0) { // set reference point, paddings int paddingRight = 50; diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index a0cd677d3d..d49fd87055 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -28,7 +28,7 @@ class SplashScreen : public QWidget Q_OBJECT public: - explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle); + explicit SplashScreen(const NetworkStyle *networkStyle); ~SplashScreen(); void setNode(interfaces::Node& node); diff --git a/src/qt/test/addressbooktests.h b/src/qt/test/addressbooktests.h index 5de89c7592..e6d24f202f 100644 --- a/src/qt/test/addressbooktests.h +++ b/src/qt/test/addressbooktests.h @@ -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 de1fbcb94c..a5c9138798 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -14,17 +14,26 @@ #include <QDir> #include <QtGlobal> -static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request) +static RPCHelpMan rpcNestedTest_rpc() { - if (request.fHelp) { - return "help message"; - } - return request.params.write(0, 0); + return RPCHelpMan{ + "rpcNestedTest", + "echo the passed string(s)", + { + {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""}, + {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""}, + {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""}, + }, + {}, + RPCExamples{""}, + [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + return request.params.write(0, 0); + }, + }; } -static const CRPCCommand vRPCCommands[] = -{ - { "test", "rpcNestedTest", &rpcNestedTest_rpc, {} }, +static const CRPCCommand vRPCCommands[] = { + {"test", "rpcNestedTest", &rpcNestedTest_rpc, {"arg1", "arg2", "arg3"}}, }; void RPCNestedTests::rpcNestedTests() @@ -34,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]"); @@ -93,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 brackts + 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 tollerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , } diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h index 0a00d1113a..320275129d 100644 --- a/src/qt/test/rpcnestedtests.h +++ b/src/qt/test/rpcnestedtests.h @@ -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/wallettests.h b/src/qt/test/wallettests.h index 8ee40bf07f..dfca3370f7 100644 --- a/src/qt/test/wallettests.h +++ b/src/qt/test/wallettests.h @@ -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/transactionview.cpp b/src/qt/transactionview.cpp index 54ecfc38ec..e14e22e9de 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -237,7 +237,7 @@ void TransactionView::setModel(WalletModel *_model) if (_model->getOptionsModel()) { // Add third party transaction URLs to context menu - QStringList listUrls = _model->getOptionsModel()->getThirdPartyTxUrls().split("|", QString::SkipEmptyParts); + QStringList listUrls = GUIUtil::SplitSkipEmptyParts(_model->getOptionsModel()->getThirdPartyTxUrls(), "|"); for (int i = 0; i < listUrls.size(); ++i) { QString url = listUrls[i].trimmed(); @@ -275,30 +275,30 @@ void TransactionView::chooseDate(int idx) break; case Today: transactionProxyModel->setDateRange( - QDateTime(current), + GUIUtil::StartOfDay(current), TransactionFilterProxy::MAX_DATE); break; case ThisWeek: { // Find last Monday QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1)); transactionProxyModel->setDateRange( - QDateTime(startOfWeek), + GUIUtil::StartOfDay(startOfWeek), TransactionFilterProxy::MAX_DATE); } break; case ThisMonth: transactionProxyModel->setDateRange( - QDateTime(QDate(current.year(), current.month(), 1)), + GUIUtil::StartOfDay(QDate(current.year(), current.month(), 1)), TransactionFilterProxy::MAX_DATE); break; case LastMonth: transactionProxyModel->setDateRange( - QDateTime(QDate(current.year(), current.month(), 1).addMonths(-1)), - QDateTime(QDate(current.year(), current.month(), 1))); + GUIUtil::StartOfDay(QDate(current.year(), current.month(), 1).addMonths(-1)), + GUIUtil::StartOfDay(QDate(current.year(), current.month(), 1))); break; case ThisYear: transactionProxyModel->setDateRange( - QDateTime(QDate(current.year(), 1, 1)), + GUIUtil::StartOfDay(QDate(current.year(), 1, 1)), TransactionFilterProxy::MAX_DATE); break; case Range: @@ -583,8 +583,8 @@ void TransactionView::dateRangeChanged() if(!transactionProxyModel) return; transactionProxyModel->setDateRange( - QDateTime(dateFrom->date()), - QDateTime(dateTo->date()).addDays(1)); + GUIUtil::StartOfDay(dateFrom->date()), + GUIUtil::StartOfDay(dateTo->date()).addDays(1)); } void TransactionView::focusTransaction(const QModelIndex &idx) diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index d9e0274d01..83f3cccbff 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 @@ -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 4a9b4a5c84..2edb7eff8a 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -196,11 +196,11 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard) } } -void WalletFrame::encryptWallet(bool status) +void WalletFrame::encryptWallet() { WalletView *walletView = currentWalletView(); if (walletView) - walletView->encryptWallet(status); + walletView->encryptWallet(); } void WalletFrame::backupWallet() diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index e2fa8055bd..271ddfc6c2 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -86,7 +86,7 @@ public Q_SLOTS: void gotoLoadPSBT(bool from_clipboard = false); /** Encrypt the wallet */ - void encryptWallet(bool status); + void encryptWallet(); /** Backup the wallet */ void backupWallet(); /** Change encrypted wallet passphrase */ diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6a3f903206..cad472b43b 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -313,12 +313,9 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const } } -bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase) +bool WalletModel::setWalletEncrypted(const SecureString& passphrase) { - if (encrypted) { - return m_wallet->encryptWallet(passphrase); - } - return false; + return m_wallet->encryptWallet(passphrase); } bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index fd52db2da3..9a3c3f2f66 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -105,7 +105,7 @@ public: SendCoinsReturn sendCoins(WalletModelTransaction &transaction); // Wallet encryption - bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); + bool setWalletEncrypted(const SecureString& passphrase); // Passphrase only needed when unlocking bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 2fc883a5f5..b1e6b43e60 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -258,11 +258,11 @@ void WalletView::updateEncryptionStatus() Q_EMIT encryptionStatusChanged(); } -void WalletView::encryptWallet(bool status) +void WalletView::encryptWallet() { if(!walletModel) return; - AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt : AskPassphraseDialog::Decrypt, this); + AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this); dlg.setModel(walletModel); dlg.exec(); diff --git a/src/qt/walletview.h b/src/qt/walletview.h index f186554758..68f8a5e95b 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -92,7 +92,7 @@ public Q_SLOTS: */ void processNewTransaction(const QModelIndex& parent, int start, int /*end*/); /** Encrypt the wallet */ - void encryptWallet(bool status); + void encryptWallet(); /** Backup the wallet */ void backupWallet(); /** Change encrypted wallet passphrase */ diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 07122b7f6d..9248db1539 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -53,7 +53,7 @@ #include <sys/vmmeter.h> #endif #endif -#ifdef __linux__ +#if defined(HAVE_STRONG_GETAUXVAL) || defined(HAVE_WEAK_GETAUXVAL) #include <sys/auxv.h> #endif @@ -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; @@ -326,7 +326,7 @@ void RandAddStaticEnv(CSHA512& hasher) // Bitcoin client version hasher << CLIENT_VERSION; -#ifdef __linux__ +#if defined(HAVE_STRONG_GETAUXVAL) || defined(HAVE_WEAK_GETAUXVAL) // Information available through getauxval() # ifdef AT_HWCAP hasher << getauxval(AT_HWCAP); @@ -346,7 +346,7 @@ void RandAddStaticEnv(CSHA512& hasher) const char* exec_str = (const char*)getauxval(AT_EXECFN); if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1); # endif -#endif // __linux__ +#endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL #ifdef HAVE_GETCPUID AddAllCPUID(hasher); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 392073d047..57327e6004 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> @@ -81,6 +82,15 @@ ChainstateManager& EnsureChainman(const util::Ref& context) 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) diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 5b362bf211..91766aacc9 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -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/mining.cpp b/src/rpc/mining.cpp index 6522c0d73e..965b278bfa 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -242,12 +242,7 @@ static RPCHelpMan generatetodescriptor() static RPCHelpMan generate() { return RPCHelpMan{"generate", "has been replaced by the -generate cli option. Refer to -help for more information.", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - - if (request.fHelp) { - throw std::runtime_error(self.ToString()); - } else { throw JSONRPCError(RPC_METHOD_NOT_FOUND, self.ToString()); - } }}; } @@ -1027,21 +1022,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, "", "", @@ -1064,7 +1057,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()) { @@ -1078,7 +1074,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 { @@ -1149,7 +1145,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()) { @@ -1166,9 +1165,9 @@ static RPCHelpMan estimaterawfee() 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); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 0c982317f5..b3102a236d 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -624,8 +624,6 @@ static RPCHelpMan echo(const std::string& name) RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - if (request.fHelp) throw std::runtime_error(self.ToString()); - if (request.params[9].isStr()) { CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug"); } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f98ea63782..6a2d1ea77f 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -126,6 +126,8 @@ 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, "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::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" @@ -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()) { @@ -215,6 +218,8 @@ static RPCHelpMan getpeerinfo() // their ver message. obj.pushKV("subver", stats.cleanSubVer); obj.pushKV("inbound", stats.fInbound); + obj.pushKV("bip152_hb_to", stats.m_bip152_highbandwidth_to); + obj.pushKV("bip152_hb_from", stats.m_bip152_highbandwidth_from); if (IsDeprecatedRPCEnabled("getpeerinfo_addnode")) { // addnode is deprecated in v0.21 for removal in v0.22 obj.pushKV("addnode", stats.m_manual_connection); @@ -288,10 +293,10 @@ static RPCHelpMan addnode() std::string strCommand; if (!request.params[1].isNull()) strCommand = request.params[1].get_str(); - if (request.fHelp || request.params.size() != 2 || - (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) + if (strCommand != "onetry" && strCommand != "add" && strCommand != "remove") { throw std::runtime_error( self.ToString()); + } NodeContext& node = EnsureNodeContext(request.context); if(!node.connman) @@ -482,12 +487,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 +582,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()); @@ -628,7 +635,7 @@ static RPCHelpMan setban() std::string strCommand; if (!request.params[1].isNull()) strCommand = request.params[1].get_str(); - if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) { + if (strCommand != "add" && strCommand != "remove") { throw std::runtime_error(help.ToString()); } NodeContext& node = EnsureNodeContext(request.context); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index c6d7fea443..f6ddaf379b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -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; @@ -287,7 +286,7 @@ 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"); } 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.h b/src/rpc/request.h index 4761e9e371..1241d999a8 100644 --- a/src/rpc/request.h +++ b/src/rpc/request.h @@ -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.h b/src/rpc/server.h index b2358ac5b2..7d13edb8b0 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -85,7 +85,6 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface); */ void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds); -typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest); typedef RPCHelpMan (*RpcMethodFnType)(); class CRPCCommand @@ -116,14 +115,6 @@ public: CHECK_NONFATAL(fn().GetArgNames() == args_in); } - //! Simplified constructor taking plain rpcfn_type function pointer. - CRPCCommand(const char* category, const char* name, rpcfn_type fn, std::initializer_list<const char*> args) - : CRPCCommand(category, name, - [fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn(request); return true; }, - {args.begin(), args.end()}, intptr_t(fn)) - { - } - std::string category; std::string name; Actor actor; diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 6c0a98cca2..e5ba9ba6d2 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; @@ -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..bb5a7158a5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -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/sigcache.cpp b/src/script/sigcache.cpp index c1786140de..582341bd3e 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -66,7 +66,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/standard.h b/src/script/standard.h index 721203385e..4d1ef61964 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -28,7 +28,7 @@ protected: public: BaseHash() : m_hash() {} - BaseHash(const HashType& in) : m_hash(in) {} + explicit BaseHash(const HashType& in) : m_hash(in) {} unsigned char* begin() { diff --git a/src/shutdown.cpp b/src/shutdown.cpp index dec497d8ec..a3321a6106 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -5,19 +5,108 @@ #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..23f84179e9 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -6,8 +6,25 @@ #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/sync.cpp b/src/sync.cpp index 322198a852..acfbe8fe29 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -13,10 +13,14 @@ #include <util/strencodings.h> #include <util/threadnames.h> +#include <boost/thread/mutex.hpp> + #include <map> +#include <mutex> #include <set> #include <system_error> #include <thread> +#include <type_traits> #include <unordered_map> #include <utility> #include <vector> @@ -135,16 +139,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 +215,41 @@ 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); +template void EnterCritical(const char*, const char*, int, boost::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..749bf5575c 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. 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/base32_tests.cpp b/src/test/base32_tests.cpp index d519eca859..3f7c5e99ee 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -6,6 +6,9 @@ #include <util/strencodings.h> #include <boost/test/unit_test.hpp> +#include <string> + +using namespace std::literals; BOOST_FIXTURE_TEST_SUITE(base32_tests, BasicTestingSetup) @@ -26,14 +29,14 @@ BOOST_AUTO_TEST_CASE(base32_testvectors) // Decoding strings with embedded NUL characters should fail bool failure; - (void)DecodeBase32(std::string("invalid", 7), &failure); - BOOST_CHECK_EQUAL(failure, true); - (void)DecodeBase32(std::string("AWSX3VPP", 8), &failure); - BOOST_CHECK_EQUAL(failure, false); - (void)DecodeBase32(std::string("AWSX3VPP\0invalid", 16), &failure); - BOOST_CHECK_EQUAL(failure, true); - (void)DecodeBase32(std::string("AWSX3VPPinvalid", 15), &failure); - BOOST_CHECK_EQUAL(failure, true); + (void)DecodeBase32("invalid\0"s, &failure); // correct size, invalid due to \0 + BOOST_CHECK(failure); + (void)DecodeBase32("AWSX3VPP"s, &failure); // valid + BOOST_CHECK(!failure); + (void)DecodeBase32("AWSX3VPP\0invalid"s, &failure); // correct size, invalid due to \0 + BOOST_CHECK(failure); + (void)DecodeBase32("AWSX3VPPinvalid"s, &failure); // invalid size + BOOST_CHECK(failure); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 6a636f2574..e55d6b3b19 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -12,7 +12,9 @@ #include <univalue.h> #include <boost/test/unit_test.hpp> +#include <string> +using namespace std::literals; extern UniValue read_json(const std::string& jsondata); @@ -58,14 +60,14 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest); } - BOOST_CHECK(!DecodeBase58("invalid", result, 100)); - BOOST_CHECK(!DecodeBase58(std::string("invalid"), result, 100)); - BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result, 100)); + BOOST_CHECK(!DecodeBase58("invalid"s, result, 100)); + BOOST_CHECK(!DecodeBase58("invalid\0"s, result, 100)); + BOOST_CHECK(!DecodeBase58("\0invalid"s, result, 100)); - BOOST_CHECK(DecodeBase58(std::string("good", 4), result, 100)); - BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result, 100)); - BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result, 100)); - BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result, 100)); + BOOST_CHECK(DecodeBase58("good"s, result, 100)); + BOOST_CHECK(!DecodeBase58("bad0IOl"s, result, 100)); + BOOST_CHECK(!DecodeBase58("goodbad0IOl"s, result, 100)); + BOOST_CHECK(!DecodeBase58("good\0bad0IOl"s, result, 100)); // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end. BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3)); @@ -73,10 +75,10 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) std::vector<unsigned char> expected = ParseHex("971a55"); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); - BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result, 100)); - BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result, 100)); - BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result, 100)); - BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result, 100)); + BOOST_CHECK(DecodeBase58Check("3vQB7B6MrGQZaxCuFg4oh"s, result, 100)); + BOOST_CHECK(!DecodeBase58Check("3vQB7B6MrGQZaxCuFg4oi"s, result, 100)); + BOOST_CHECK(!DecodeBase58Check("3vQB7B6MrGQZaxCuFg4oh0IOl"s, result, 100)); + BOOST_CHECK(!DecodeBase58Check("3vQB7B6MrGQZaxCuFg4oh\0" "0IOl"s, result, 100)); } BOOST_AUTO_TEST_CASE(base58_random_encode_decode) diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp index 5927eab6cf..bb8d102bd0 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -6,6 +6,9 @@ #include <util/strencodings.h> #include <boost/test/unit_test.hpp> +#include <string> + +using namespace std::literals; BOOST_FIXTURE_TEST_SUITE(base64_tests, BasicTestingSetup) @@ -23,14 +26,14 @@ BOOST_AUTO_TEST_CASE(base64_testvectors) // Decoding strings with embedded NUL characters should fail bool failure; - (void)DecodeBase64(std::string("invalid", 7), &failure); - BOOST_CHECK_EQUAL(failure, true); - (void)DecodeBase64(std::string("nQB/pZw=", 8), &failure); - BOOST_CHECK_EQUAL(failure, false); - (void)DecodeBase64(std::string("nQB/pZw=\0invalid", 16), &failure); - BOOST_CHECK_EQUAL(failure, true); - (void)DecodeBase64(std::string("nQB/pZw=invalid", 15), &failure); - BOOST_CHECK_EQUAL(failure, true); + (void)DecodeBase64("invalid\0"s, &failure); + BOOST_CHECK(failure); + (void)DecodeBase64("nQB/pZw="s, &failure); + BOOST_CHECK(!failure); + (void)DecodeBase64("nQB/pZw=\0invalid"s, &failure); + BOOST_CHECK(failure); + (void)DecodeBase64("nQB/pZw=invalid\0"s, &failure); + BOOST_CHECK(failure); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 173ec5e3d9..6bfcf242d0 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -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/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index c399da900f..8f6fdd04d0 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -80,7 +80,8 @@ 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 = std::make_unique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -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 = std::make_unique<PeerManager>(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,7 +224,8 @@ 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 = std::make_unique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); banman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -268,7 +271,8 @@ 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 = std::make_unique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, + *m_node.chainman, *m_node.mempool, false); banman->ClearBanned(); int64_t nStartTime = GetTime(); 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..af9080b5e9 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)) { 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..eb3424ef28 100644 --- a/src/test/fuzz/autofile.cpp +++ b/src/test/fuzz/autofile.cpp @@ -15,7 +15,7 @@ #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); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index fc4a1d9261..cf69fa0722 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -24,14 +24,15 @@ int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept } } // namespace -void initialize() +void initialize_banman() { InitializeFuzzingContext(); } -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); { 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..b1a485e12e 100644 --- a/src/test/fuzz/bech32.cpp +++ b/src/test/fuzz/bech32.cpp @@ -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..c0c66c564b 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()); diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp index e575640be5..23e197456a 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); 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..1ae421493e 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -34,14 +34,14 @@ 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); } -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; diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 6521c3f3b2..c5702cf98e 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(); } -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,7 +32,7 @@ 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)) { + switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 28)) { case 0: random_address = ConsumeAddress(fuzzed_data_provider); break; @@ -127,25 +128,19 @@ void test_one_input(const std::vector<uint8_t>& buffer) 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: + case 27: connman.SetServices(random_service, static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>())); break; - case 30: + case 28: connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool()); break; } } (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..4783cc1c43 100644 --- a/src/test/fuzz/crypto.cpp +++ b/src/test/fuzz/crypto.cpp @@ -17,7 +17,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); 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..d751466f11 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()}; diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp index 48e4263f27..631af9c70d 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()}; 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/decode_tx.cpp b/src/test/fuzz/decode_tx.cpp index a2b18c0365..249f5a3cda 100644 --- a/src/test/fuzz/decode_tx.cpp +++ b/src/test/fuzz/decode_tx.cpp @@ -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..544379c0b0 100644 --- a/src/test/fuzz/fuzz.h +++ b/src/test/fuzz/fuzz.h @@ -5,10 +5,29 @@ #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); + +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..0656ddc547 100644 --- a/src/test/fuzz/kitchen_sink.cpp +++ b/src/test/fuzz/kitchen_sink.cpp @@ -15,7 +15,7 @@ // 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()); diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index d9de9d9866..c428a86631 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(); } -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); 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..15bcfab3ad 100644 --- a/src/test/fuzz/merkleblock.cpp +++ b/src/test/fuzz/merkleblock.cpp @@ -13,7 +13,7 @@ #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; 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/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 c61d406291..66d7c512e4 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -19,15 +19,15 @@ #include <string> #include <vector> -void initialize() +void initialize_net() { static const BasicTestingSetup basic_testing_setup; } -void test_one_input(const std::vector<uint8_t>& buffer) +FUZZ_TARGET_INIT(net, initialize_net) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - + SetMockTime(ConsumeTime(fuzzed_data_provider)); const std::optional<CAddress> address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); if (!address) { return; @@ -48,8 +48,9 @@ void test_one_input(const std::vector<uint8_t>& buffer) 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()}; + node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>()); while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) { + switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 10)) { case 0: { node.CloseSocketDisconnect(); break; @@ -59,10 +60,6 @@ void test_one_input(const std::vector<uint8_t>& buffer) 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; @@ -71,18 +68,18 @@ void test_one_input(const std::vector<uint8_t>& buffer) node.copyStats(stats, asmap); break; } - case 4: { + case 3: { const CNode* add_ref_node = node.AddRef(); assert(add_ref_node == &node); break; } - case 5: { + case 4: { if (node.GetRefCount() > 0) { node.Release(); } break; } - case 6: { + case 5: { if (node.m_addr_known == nullptr) { break; } @@ -93,7 +90,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) node.AddAddressKnown(*addr_opt); break; } - case 7: { + case 6: { if (node.m_addr_known == nullptr) { break; } @@ -105,7 +102,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) node.PushAddress(*addr_opt, fast_random_context); break; } - case 8: { + case 7: { const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider); if (!inv_opt) { break; @@ -113,11 +110,11 @@ void test_one_input(const std::vector<uint8_t>& buffer) node.AddKnownTx(inv_opt->hash); break; } - case 9: { + case 8: { node.PushTxInventory(ConsumeUInt256(fuzzed_data_provider)); break; } - case 10: { + case 9: { const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider); if (!service_opt) { break; @@ -125,10 +122,10 @@ void test_one_input(const std::vector<uint8_t>& buffer) node.SetAddrLocal(*service_opt); break; } - case 11: { + case 10: { const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); bool complete; - node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete); + node.ReceiveMsgBytes(b, complete); break; } } diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp index 8a674ac1e9..3620e16d30 100644 --- a/src/test/fuzz/net_permissions.cpp +++ b/src/test/fuzz/net_permissions.cpp @@ -13,7 +13,7 @@ #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); 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/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp index 7e216e16fe..6ba75309c8 100644 --- a/src/test/fuzz/p2p_transport_deserializer.cpp +++ b/src/test/fuzz/p2p_transport_deserializer.cpp @@ -12,24 +12,21 @@ #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}; - const char* pch = (const char*)buffer.data(); - size_t n_bytes = buffer.size(); - while (n_bytes > 0) { - const int handled = deserializer.Read(pch, n_bytes); + Span<const uint8_t> msg_bytes{buffer}; + while (msg_bytes.size() > 0) { + const int handled = deserializer.Read(msg_bytes); if (handled < 0) { break; } - pch += handled; - n_bytes -= handled; if (deserializer.Complete()) { const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()}; uint32_t out_err_raw_size{0}; diff --git a/src/test/fuzz/parse_hd_keypath.cpp b/src/test/fuzz/parse_hd_keypath.cpp index f668ca8c48..7d0d5643bf 100644 --- a/src/test/fuzz/parse_hd_keypath.cpp +++ b/src/test/fuzz/parse_hd_keypath.cpp @@ -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..4d5fa70dfa 100644 --- a/src/test/fuzz/parse_iso8601.cpp +++ b/src/test/fuzz/parse_iso8601.cpp @@ -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..89d9be392e 100644 --- a/src/test/fuzz/parse_numbers.cpp +++ b/src/test/fuzz/parse_numbers.cpp @@ -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..1382afbc2c 100644 --- a/src/test/fuzz/parse_script.cpp +++ b/src/test/fuzz/parse_script.cpp @@ -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..8a17a4b51b 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -14,12 +14,12 @@ #include <string> #include <vector> -void initialize() +void initialize_policy_estimator() { InitializeFuzzingContext(); } -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; diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp index 0edcf201c7..8fa52143d8 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(); } -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..02beb6eb37 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(); 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..01de8bdbb5 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -32,19 +32,10 @@ #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, @@ -60,7 +51,7 @@ void initialize() 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(); @@ -88,3 +79,29 @@ void test_one_input(const std::vector<uint8_t>& buffer) 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..e12e780a18 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -19,7 +19,7 @@ const TestingSetup* g_setup; -void initialize() +void initialize_process_messages() { static TestingSetup setup{ CBaseChainParams::REGTEST, @@ -35,7 +35,7 @@ void initialize() 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()); 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..6087ee964a 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()); diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp index 4274fa4351..892af655f6 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); 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..d232e984bc 100644 --- a/src/test/fuzz/script_ops.cpp +++ b/src/test/fuzz/script_ops.cpp @@ -11,7 +11,7 @@ #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); 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..650318f13c 100644 --- a/src/test/fuzz/scriptnum_ops.cpp +++ b/src/test/fuzz/scriptnum_ops.cpp @@ -24,7 +24,7 @@ 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); 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..541322d484 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); } -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..293a7e7e90 100644 --- a/src/test/fuzz/spanparsing.cpp +++ b/src/test/fuzz/spanparsing.cpp @@ -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..4af0e750ce 100644 --- a/src/test/fuzz/strprintf.cpp +++ b/src/test/fuzz/strprintf.cpp @@ -13,7 +13,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); diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 01b523cee4..375a8c1ed0 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{}; 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..dd94922b86 100644 --- a/src/test/fuzz/tx_in.cpp +++ b/src/test/fuzz/tx_in.cpp @@ -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..5e22c4adc5 100644 --- a/src/test/fuzz/tx_out.cpp +++ b/src/test/fuzz/tx_out.cpp @@ -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.h b/src/test/fuzz/util.h index e99ed8d72d..cf666a8b93 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -35,23 +35,23 @@ #include <string> #include <vector> -NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept +[[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const 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 {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 +62,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 +73,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 +86,36 @@ NODISCARD inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzz return obj; } -NODISCARD inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[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 +124,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 +133,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,7 +153,7 @@ 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)) { @@ -190,7 +191,7 @@ NODISCARD inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_ } 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 +214,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 +224,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 +239,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); @@ -419,7 +420,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,7 +441,7 @@ public: } }; -NODISCARD inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept +[[nodiscard]] inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider& fuzzed_data_provider) noexcept { return {fuzzed_data_provider}; } 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/net_tests.cpp b/src/test/net_tests.cpp index 37eca8b7ef..beac65942e 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,10 +22,13 @@ #include <boost/test/unit_test.hpp> +#include <algorithm> #include <ios> #include <memory> #include <string> +using namespace std::literals; + class CAddrManSerializationMock : public CAddrMan { public: @@ -73,7 +77,7 @@ public: } }; -static CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman) +static CDataStream AddrmanToStream(const CAddrManSerializationMock& _addrman) { CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); ssPeersIn << Params().MessageStart(); @@ -106,8 +110,8 @@ BOOST_AUTO_TEST_CASE(caddrdb_read) BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false)); BOOST_CHECK(Lookup("250.7.2.2", addr2, 9999, false)); BOOST_CHECK(Lookup("250.7.3.3", addr3, 9999, false)); - BOOST_CHECK(Lookup(std::string("250.7.3.3", 9), addr3, 9999, false)); - BOOST_CHECK(!Lookup(std::string("250.7.3.3\0example.com", 21), addr3, 9999, false)); + BOOST_CHECK(Lookup("250.7.3.3"s, addr3, 9999, false)); + BOOST_CHECK(!Lookup("250.7.3.3\0example.com"s, addr3, 9999, false)); // Add three addresses to new table. CService source; @@ -602,6 +606,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 @@ -769,4 +783,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 f5d26fafef..ac4db3a5b6 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -16,6 +16,8 @@ #include <boost/test/unit_test.hpp> +using namespace std::literals; + BOOST_FIXTURE_TEST_SUITE(netbase_tests, BasicTestingSetup) static CNetAddr ResolveIP(const std::string& ip) @@ -431,20 +433,20 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters) { CNetAddr addr; - BOOST_CHECK(LookupHost(std::string("127.0.0.1", 9), addr, false)); - BOOST_CHECK(!LookupHost(std::string("127.0.0.1\0", 10), addr, false)); - BOOST_CHECK(!LookupHost(std::string("127.0.0.1\0example.com", 21), addr, false)); - BOOST_CHECK(!LookupHost(std::string("127.0.0.1\0example.com\0", 22), addr, false)); + BOOST_CHECK(LookupHost("127.0.0.1"s, addr, false)); + BOOST_CHECK(!LookupHost("127.0.0.1\0"s, addr, false)); + BOOST_CHECK(!LookupHost("127.0.0.1\0example.com"s, addr, false)); + BOOST_CHECK(!LookupHost("127.0.0.1\0example.com\0"s, addr, false)); CSubNet ret; - BOOST_CHECK(LookupSubNet(std::string("1.2.3.0/24", 10), ret)); - BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0", 11), ret)); - BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com", 22), ret)); - BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com\0", 23), ret)); + BOOST_CHECK(LookupSubNet("1.2.3.0/24"s, ret)); + 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(std::string("5wyqrzbvrdsumnok.onion", 22), ret)); - BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0", 23), ret)); - BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com", 34), ret)); - BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret)); + 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)); } // Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only 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/sync_tests.cpp b/src/test/sync_tests.cpp index 19029ebd3c..71275f69d9 100644 --- a/src/test/sync_tests.cpp +++ b/src/test/sync_tests.cpp @@ -6,6 +6,9 @@ #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> +#include <boost/thread/mutex.hpp> + +#include <mutex> namespace { template <typename MutexType> @@ -29,6 +32,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 +101,48 @@ 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_boost_mutex) +{ + TestDoubleLock<boost::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/util/logging.h b/src/test/util/logging.h index 1fcf7ca305..a49f9a7292 100644 --- a/src/test/util/logging.h +++ b/src/test/util/logging.h @@ -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/net.cpp b/src/test/util/net.cpp index 09f2f1807f..847a490e03 100644 --- a/src/test/util/net.cpp +++ b/src/test/util/net.cpp @@ -7,9 +7,9 @@ #include <chainparams.h> #include <net.h> -void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, const char* pch, unsigned int nBytes, bool& complete) const +void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, Span<const uint8_t> msg_bytes, bool& complete) const { - assert(node.ReceiveMsgBytes(pch, nBytes, complete)); + assert(node.ReceiveMsgBytes(msg_bytes, complete)); if (complete) { size_t nSizeAdded = 0; auto it(node.vRecvMsg.begin()); @@ -29,11 +29,11 @@ void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, const char* pch, unsigned bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) const { - std::vector<unsigned char> ser_msg_header; + std::vector<uint8_t> ser_msg_header; node.m_serializer->prepareForTransport(ser_msg, ser_msg_header); bool complete; - NodeReceiveMsgBytes(node, (const char*)ser_msg_header.data(), ser_msg_header.size(), complete); - NodeReceiveMsgBytes(node, (const char*)ser_msg.data.data(), ser_msg.data.size(), complete); + NodeReceiveMsgBytes(node, ser_msg_header, complete); + NodeReceiveMsgBytes(node, ser_msg.data, complete); return complete; } diff --git a/src/test/util/net.h b/src/test/util/net.h index ca8cb7fad5..1208e92762 100644 --- a/src/test/util/net.h +++ b/src/test/util/net.h @@ -25,7 +25,7 @@ struct ConnmanTestMsg : public CConnman { void ProcessMessagesOnce(CNode& node) { m_msgproc->ProcessMessages(&node, flagInterruptMsgProc); } - void NodeReceiveMsgBytes(CNode& node, const char* pch, unsigned int nBytes, bool& complete) const; + void NodeReceiveMsgBytes(CNode& node, Span<const uint8_t> msg_bytes, bool& complete) const; bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) const; }; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 2d3137e1e2..db8b43d039 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,41 +125,21 @@ 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. + m_node.scheduler = MakeUnique<CScheduler>(); threadGroup.create_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; - m_node.chainman->InitializeChainstate(*m_node.mempool); - ::ChainstateActive().InitCoinsDB( - /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); - assert(!::ChainstateActive().CanFlushToDisk()); - ::ChainstateActive().InitCoinsCache(1 << 23); - assert(::ChainstateActive().CanFlushToDisk()); - if (!LoadGenesisBlock(chainparams)) { - throw std::runtime_error("LoadGenesisBlock failed."); - } - - BlockValidationState state; - if (!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; @@ -166,18 +147,9 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const 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); - { - CConnman::Options options; - options.m_msgproc = m_node.peerman.get(); - m_node.connman->Init(options); - } } -TestingSetup::~TestingSetup() +ChainTestingSetup::~ChainTestingSetup() { if (m_node.scheduler) m_node.scheduler->stop(); threadGroup.interrupt_all(); @@ -195,6 +167,41 @@ TestingSetup::~TestingSetup() 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); + assert(!::ChainstateActive().CanFlushToDisk()); + ::ChainstateActive().InitCoinsCache(1 << 23); + assert(::ChainstateActive().CanFlushToDisk()); + if (!LoadGenesisBlock(chainparams)) { + throw std::runtime_error("LoadGenesisBlock failed."); + } + + BlockValidationState state; + if (!ActivateBestChain(state, chainparams)) { + throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString())); + } + + m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); + m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. + m_node.peerman = std::make_unique<PeerManager>(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(); + m_node.connman->Init(options); + } +} + TestChain100Setup::TestChain100Setup() { // Generate a 100-block chain: diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 1812ce1666..0498e7d182 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -83,14 +83,21 @@ 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 { +struct ChainTestingSetup : public BasicTestingSetup { boost::thread_group threadGroup; + 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 */ diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 010b6adf1f..a9ef0f73cc 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -23,6 +23,7 @@ #include <array> #include <stdint.h> +#include <string.h> #include <thread> #include <univalue.h> #include <utility> @@ -35,6 +36,8 @@ #include <boost/test/unit_test.hpp> +using namespace std::literals; + /* defined in logging.cpp */ namespace BCLog { std::string LogEscapeMessage(const std::string& str); @@ -228,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; } @@ -1257,9 +1260,9 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney) BOOST_CHECK(!ParseMoney("-1", ret)); // Parsing strings with embedded NUL characters should fail - BOOST_CHECK(!ParseMoney(std::string("\0-1", 3), ret)); - BOOST_CHECK(!ParseMoney(std::string("\01", 2), ret)); - BOOST_CHECK(!ParseMoney(std::string("1\0", 2), ret)); + BOOST_CHECK(!ParseMoney("\0-1"s, ret)); + BOOST_CHECK(!ParseMoney("\0" "1"s, ret)); + BOOST_CHECK(!ParseMoney("1\0"s, ret)); } BOOST_AUTO_TEST_CASE(util_IsHex) @@ -1420,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 @@ -1479,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)); @@ -1593,9 +1613,9 @@ BOOST_AUTO_TEST_CASE(test_FormatSubVersion) std::vector<std::string> comments2; comments2.push_back(std::string("comment1")); comments2.push_back(SanitizeString(std::string("Comment2; .,_?@-; !\"#$%&'()*+/<=>[]\\^`{|}~"), SAFE_CHARS_UA_COMMENT)); // Semicolon is discouraged but not forbidden by BIP-0014 - BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector<std::string>()),std::string("/Test:0.9.99/")); - BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:0.9.99(comment1)/")); - BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2),std::string("/Test:0.9.99(comment1; Comment2; .,_?@-; )/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector<std::string>()),std::string("/Test:9.99.0/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:9.99.0(comment1)/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2),std::string("/Test:9.99.0(comment1; Comment2; .,_?@-; )/")); } BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) @@ -1840,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()); } diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 36badafc4e..75939e0140 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -15,15 +15,16 @@ #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(); @@ -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; @@ -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(); } @@ -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/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..9d91f42b1b 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -7,6 +7,7 @@ #include <chainparams.h> #include <chainparamsbase.h> +#include <compat.h> #include <crypto/hmac_sha256.h> #include <net.h> #include <netaddress.h> diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 0c2b731967..d18182c07d 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,13 +614,11 @@ 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; + 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; diff --git a/src/txmempool.h b/src/txmempool.h index f513f14af6..15797cbc00 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -488,8 +488,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 +498,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 +601,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 @@ -611,7 +617,6 @@ public: * 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); } // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of @@ -850,7 +855,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/univalue/include/univalue.h b/src/univalue/include/univalue.h index 6080516353..048e162f7d 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -100,6 +100,10 @@ public: UniValue tmpVal(val_); return push_back(tmpVal); } + bool push_back(bool val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } bool push_back(int val_) { UniValue tmpVal(val_); return push_back(tmpVal); @@ -129,7 +133,7 @@ public: return pushKV(key, tmpVal); } bool pushKV(const std::string& key, bool val_) { - UniValue tmpVal((bool)val_); + UniValue tmpVal(val_); return pushKV(key, tmpVal); } bool pushKV(const std::string& key, int val_) { diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp index 70ccc0d08a..ccc1344836 100644 --- a/src/univalue/test/object.cpp +++ b/src/univalue/test/object.cpp @@ -210,19 +210,31 @@ BOOST_AUTO_TEST_CASE(univalue_array) BOOST_CHECK(arr.push_back((int64_t) -400LL)); BOOST_CHECK(arr.push_back((int) -401)); BOOST_CHECK(arr.push_back(-40.1)); + BOOST_CHECK(arr.push_back(true)); BOOST_CHECK_EQUAL(arr.empty(), false); - BOOST_CHECK_EQUAL(arr.size(), 9); + BOOST_CHECK_EQUAL(arr.size(), 10); BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); + BOOST_CHECK_EQUAL(arr[0].getType(), UniValue::VNUM); BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); + BOOST_CHECK_EQUAL(arr[1].getType(), UniValue::VSTR); BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); + BOOST_CHECK_EQUAL(arr[2].getType(), UniValue::VSTR); BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); + BOOST_CHECK_EQUAL(arr[3].getType(), UniValue::VSTR); BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); + BOOST_CHECK_EQUAL(arr[4].getType(), UniValue::VSTR); BOOST_CHECK_EQUAL(arr[5].getValStr(), "400"); + BOOST_CHECK_EQUAL(arr[5].getType(), UniValue::VNUM); BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400"); + BOOST_CHECK_EQUAL(arr[6].getType(), UniValue::VNUM); BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401"); + BOOST_CHECK_EQUAL(arr[7].getType(), UniValue::VNUM); BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1"); + BOOST_CHECK_EQUAL(arr[8].getType(), UniValue::VNUM); + BOOST_CHECK_EQUAL(arr[9].getValStr(), "1"); + BOOST_CHECK_EQUAL(arr[9].getType(), UniValue::VBOOL); BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); 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..e7620d97a0 100644 --- a/src/util/check.h +++ b/src/util/check.h @@ -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/memory.h b/src/util/memory.h index 15ecf8f80d..4d73b32869 100644 --- a/src/util/memory.h +++ b/src/util/memory.h @@ -10,10 +10,11 @@ #include <utility> //! Substitute for C++14 std::make_unique. +//! DEPRECATED use std::make_unique in new code. template <typename T, typename... Args> std::unique_ptr<T> MakeUnique(Args&&... args) { - return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + return std::make_unique<T>(std::forward<Args>(args)...); } #endif 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..8ee43c620b 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. @@ -170,7 +170,7 @@ bool TimingResistantEqual(const T& a, const T& b) * @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.h b/src/util/system.h index 1df194ca84..2be8bb754b 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -60,7 +60,7 @@ bool FileCommit(FILE *file); 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 +188,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 +220,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 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/validation.cpp b/src/validation.cpp index feb7502a0f..2585345dee 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; @@ -449,7 +446,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 +479,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 +502,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); // 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); // Try to add the transaction to the mempool, removing any conflicts first. // Returns true if the transaction is in the mempool after any size @@ -696,7 +693,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 +918,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 +945,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; @@ -1312,8 +1309,6 @@ bool CChainState::IsInitialBlockDownload() const return false; } -static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr; - static void AlertNotify(const std::string& strMessage) { uiInterface.NotifyAlertChanged(); @@ -1339,73 +1334,16 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main) AssertLockHeld(cs_main); // Before we get past initial download, we cannot reliably alert about forks // (we assume we don't get stuck on a fork before finishing our initial sync) - if (::ChainstateActive().IsInitialBlockDownload()) + if (::ChainstateActive().IsInitialBlockDownload()) { return; - - // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) - // of our head, drop it - if (pindexBestForkTip && ::ChainActive().Height() - pindexBestForkTip->nHeight >= 72) - pindexBestForkTip = nullptr; - - if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6))) - { - if (!GetfLargeWorkForkFound() && pindexBestForkBase) - { - std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); - AlertNotify(warning); - } - if (pindexBestForkTip && pindexBestForkBase) - { - LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); - SetfLargeWorkForkFound(true); - } - else - { - LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); - SetfLargeWorkInvalidChainFound(true); - } - } - else - { - SetfLargeWorkForkFound(false); - SetfLargeWorkInvalidChainFound(false); } -} -static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) EXCLUSIVE_LOCKS_REQUIRED(cs_main) -{ - AssertLockHeld(cs_main); - // If we are on a fork that is sufficiently large, set a warning flag - CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = ::ChainActive().Tip(); - while (pfork && pfork != plonger) - { - while (plonger && plonger->nHeight > pfork->nHeight) - plonger = plonger->pprev; - if (pfork == plonger) - break; - pfork = pfork->pprev; - } - - // We define a condition where we should warn the user about as a fork of at least 7 blocks - // with a tip within 72 blocks (+/- 12 hours if no one mines it) of ours - // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network - // hash rate operating on the fork. - // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) - // We define it this way because it allows us to only store the highest fork tip (+ base) which meets - // the 7-block condition and from this always have the most-likely-to-cause-warning fork - if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) && - pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && - ::ChainActive().Height() - pindexNewForkTip->nHeight < 72) - { - pindexBestForkTip = pindexNewForkTip; - pindexBestForkBase = pfork; + if (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)) { + LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); + SetfLargeWorkInvalidChainFound(true); + } else { + SetfLargeWorkInvalidChainFound(false); } - - CheckForkWarningConditions(); } // Called both upon regular invalid block discovery *and* InvalidateBlock @@ -2457,9 +2395,7 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C } bilingual_str warning_messages; - int num_unexpected_version = 0; - if (!::ChainstateActive().IsInitialBlockDownload()) - { + if (!::ChainstateActive().IsInitialBlockDownload()) { const CBlockIndex* pindex = pindexNew; for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { WarningBitsConditionChecker checker(bit); @@ -2473,14 +2409,6 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C } } } - // Check the version of the last 100 blocks to see if we need to upgrade: - for (int i = 0; i < 100 && pindex != nullptr; i++) - { - int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); - if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) - ++num_unexpected_version; - pindex = pindex->pprev; - } } LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, @@ -2488,10 +2416,6 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C FormatISO8601DateTime(pindexNew->GetBlockTime()), GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(), !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : ""); - - if (num_unexpected_version > 0) { - LogPrint(BCLog::VALIDATION, "%d of last 100 blocks have unexpected version\n", num_unexpected_version); - } } /** Disconnect m_chain's tip. @@ -2746,8 +2670,8 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); - const CBlockIndex *pindexOldTip = m_chain.Tip(); - const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork); + const CBlockIndex* pindexOldTip = m_chain.Tip(); + const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork); // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; @@ -2767,7 +2691,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai fBlocksDisconnected = true; } - // Build list of new blocks to connect. + // Build list of new blocks to connect (in descending height order). std::vector<CBlockIndex*> vpindexToConnect; bool fContinue = true; int nHeight = pindexFork ? pindexFork->nHeight : -1; @@ -2777,7 +2701,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); vpindexToConnect.clear(); vpindexToConnect.reserve(nTargetHeight - nHeight); - CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + CBlockIndex* pindexIter = pindexMostWork->GetAncestor(nTargetHeight); while (pindexIter && pindexIter->nHeight != nHeight) { vpindexToConnect.push_back(pindexIter); pindexIter = pindexIter->pprev; @@ -2785,7 +2709,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai nHeight = nTargetHeight; // Connect new blocks. - for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { + for (CBlockIndex* pindexConnect : reverse_iterate(vpindexToConnect)) { if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. @@ -2821,11 +2745,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai } m_mempool.check(&CoinsTip()); - // Callbacks/notifications for a new best chain. - if (fInvalidFound) - CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); - else - CheckForkWarningConditions(); + CheckForkWarningConditions(); return true; } @@ -5187,7 +5107,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) { diff --git a/src/validation.h b/src/validation.h index ffb038ad75..d10b260d8a 100644 --- a/src/validation.h +++ b/src/validation.h @@ -42,7 +42,6 @@ class CChainParams; class CInv; class CConnman; class CScriptCheck; -class CBlockPolicyEstimator; class CTxMemPool; class ChainstateManager; class TxValidationState; @@ -110,7 +109,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; @@ -564,7 +562,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; } diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 85aae0170d..6ed48593fb 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) { @@ -808,21 +805,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 +829,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..bf1617d67f 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,9 +223,6 @@ public: std::string BerkeleyDatabaseVersion(); -//! Check if Berkeley database exists at specified path. -bool ExistsBerkeleyDatabase(const fs::path& path); - //! Return object giving access to Berkeley database at specified path. std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); 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/feebumper.cpp b/src/wallet/feebumper.cpp index 6cbad14de8..1bbfa197d7 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; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 8b2ef191fb..085dde1026 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -15,6 +15,9 @@ #include <util/moneystr.h> #include <util/system.h> #include <util/translation.h> +#ifdef USE_BDB +#include <wallet/bdb.h> +#endif #include <wallet/coincontrol.h> #include <wallet/wallet.h> #include <walletinitinterface.h> @@ -68,9 +71,14 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const #endif argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); +#ifdef USE_BDB argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); +#else + argsman.AddHiddenArgs({"-dblogsize", "-flushwallet", "-privdb"}); +#endif + argsman.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); argsman.AddHiddenArgs({"-zapwallettxes"}); diff --git a/src/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/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 6efa3d0c27..94a73b67df 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -29,7 +29,6 @@ #include <util/translation.h> #include <util/url.h> #include <util/vector.h> -#include <validation.h> #include <wallet/coincontrol.h> #include <wallet/context.h> #include <wallet/feebumper.h> @@ -198,35 +197,27 @@ 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"); } - CFeeRate fee_rate_in_sat_vb{CFeeRate(AmountFromValue(fee_rate), COIN)}; - if (override_min_fee) { - if (fee_rate_in_sat_vb <= CFeeRate(0)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid fee_rate %s (must be greater than 0)", fee_rate_in_sat_vb.ToString(FeeEstimateMode::SAT_VB))); - } - cc.fOverrideFeeRate = true; - } - cc.m_feerate = fee_rate_in_sat_vb; + cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN); + if (override_min_fee) cc.fOverrideFeeRate = true; // Default RBF to true for explicit fee_rate, if unset. if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true; return; @@ -235,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()); } } @@ -473,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" @@ -514,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); @@ -942,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); @@ -2546,7 +2537,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); @@ -2764,6 +2755,12 @@ static RPCHelpMan createwallet() warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet")); } +#ifndef USE_BDB + if (!(flags & WALLET_FLAG_DESCRIPTORS)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without bdb support (required for legacy wallets)"); + } +#endif + DatabaseOptions options; DatabaseStatus status; options.require_create = true; @@ -2792,7 +2789,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, "", "", { @@ -2806,8 +2803,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(); @@ -3150,11 +3147,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f if (options.exists("estimate_mode")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate"); } - CFeeRate fee_rate(AmountFromValue(options["feeRate"])); - if (fee_rate <= CFeeRate(0)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid feeRate %s (must be greater than 0)", fee_rate.ToString(FeeEstimateMode::BTC_KVB))); - } - coinControl.m_feerate = fee_rate; + coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"])); coinControl.fOverrideFeeRate = true; } @@ -3164,7 +3157,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 @@ -3393,7 +3386,7 @@ RPCHelpMan signrawtransactionwithwallet() static RPCHelpMan bumpfee_helper(std::string method_name) { bool want_psbt = method_name == "psbtbumpfee"; - const std::string incremental_fee{CFeeRate(DEFAULT_MIN_RELAY_TX_FEE).ToString(FeeEstimateMode::SAT_VB)}; + const std::string incremental_fee{CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE).ToString(FeeEstimateMode::SAT_VB)}; return RPCHelpMan{method_name, "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n" @@ -3416,7 +3409,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name) {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks\n"}, {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation", "\nSpecify a fee rate in " + CURRENCY_ATOM + "/vB instead of relying on the built-in fee estimator.\n" - "Must be at least " + incremental_fee + " " + CURRENCY_ATOM + "/vB higher than the current transaction fee rate.\n" + "Must be at least " + incremental_fee + " higher than the current transaction fee rate.\n" "WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB.\n"}, {"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n" "marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n" @@ -3492,7 +3485,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 @@ -4081,10 +4074,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" @@ -4096,7 +4089,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 ); @@ -4241,7 +4234,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); @@ -4472,14 +4465,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)"} }, }, @@ -4502,11 +4499,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; diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp index 225b975067..09a9ec68cd 100644 --- a/src/wallet/salvage.cpp +++ b/src/wallet/salvage.cpp @@ -6,6 +6,7 @@ #include <fs.h> #include <streams.h> #include <util/translation.h> +#include <wallet/bdb.h> #include <wallet/salvage.h> #include <wallet/wallet.h> #include <wallet/walletdb.h> @@ -27,11 +28,13 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v DatabaseStatus status; options.require_existing = true; options.verify = false; + options.require_format = DatabaseFormat::BERKELEY; 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 d2e1be6402..15972fe7bb 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -3,12 +3,15 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <key_io.h> +#include <logging.h> #include <outputtype.h> #include <script/descriptor.h> #include <script/sign.h> #include <util/bip32.h> #include <util/strencodings.h> #include <util/string.h> +#include <util/system.h> +#include <util/time.h> #include <util/translation.h> #include <wallet/scriptpubkeyman.h> @@ -453,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 3bf8f78120..cec46a0fbb 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -11,6 +11,7 @@ #include <script/standard.h> #include <util/error.h> #include <util/message.h> +#include <util/time.h> #include <wallet/crypter.h> #include <wallet/ismine.h> #include <wallet/walletdb.h> @@ -171,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; } @@ -503,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 d83332c194..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; @@ -206,7 +205,9 @@ void SQLiteDatabase::Open() } if (m_db == nullptr) { - TryCreateDirectories(m_dir_path); + if (!m_mock) { + TryCreateDirectories(m_dir_path); + } int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr); if (ret != SQLITE_OK) { throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret))); @@ -566,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; @@ -594,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..442563184e 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -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..019161415c 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -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) { @@ -283,7 +284,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) // Make sure that can use BnB when there are preset inputs empty_wallet(); { - std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase()); + std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase()); bool firstRun; wallet->LoadWallet(firstRun); wallet->SetupLegacyScriptPubKeyMan(); diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index 8f0083cd2e..1a28852a6b 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -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..334e4ae0d8 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -10,7 +10,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) { - m_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; diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h index f5bade77df..f666c45a34 100644 --- a/src/wallet/test/init_test_fixture.h +++ b/src/wallet/test/init_test_fixture.h @@ -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/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp index d5aed99d99..0ef8b9c4bf 100644 --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -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_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..a6db261914 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; }; @@ -612,9 +601,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) 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 new file mode 100644 index 0000000000..a3859e2e4b --- /dev/null +++ b/src/wallet/test/walletdb_tests.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/util/setup_common.h> +#include <clientversion.h> +#include <streams.h> +#include <uint256.h> + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(walletdb_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue) +{ + /** + * When ReadKeyValue() reads from either a "key" or "wkey" it first reads the CDataStream steam into a + * CPrivKey or CWalletKey respectively and then reads a hash of the pubkey and privkey into a uint256. + * Wallets from 0.8 or before do not store the pubkey/privkey hash, trying to read the hash from old + * wallets throws an exception, for backwards compatibility this read is wrapped in a try block to + * silently fail. The test here makes sure the type of exception thrown from CDataStream::read() + * matches the type we expect, otherwise we need to update the "key"/"wkey" exception type caught. + */ + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + uint256 dummy; + BOOST_CHECK_THROW(ssValue >> dummy, std::ios_base::failure); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ff8bfff872..3e37491a23 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); @@ -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) @@ -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; @@ -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; @@ -1446,13 +1447,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 +1492,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 +1583,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 +1772,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 +1802,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; @@ -3109,13 +3113,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 +3182,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 +3209,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 +3221,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 +3259,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) @@ -4008,7 +4013,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)) { @@ -4058,9 +4063,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st 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 +4074,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 +4114,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 +4123,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 +4152,7 @@ void CWallet::postInitProcess() bool CWallet::BackupWallet(const std::string& strDest) const { - return database->Backup(strDest); + return GetDatabase().Backup(strDest); } CKeyPool::CKeyPool() @@ -4453,7 +4455,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..e6beb111fb 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -695,7 +695,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 +729,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 +755,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)) { } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index aa3b3c10b0..5b72a01939 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -14,7 +14,9 @@ #include <util/system.h> #include <util/time.h> #include <util/translation.h> +#ifdef USE_BDB #include <wallet/bdb.h> +#endif #ifdef USE_SQLITE #include <wallet/sqlite.h> #endif @@ -362,7 +364,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> hash; } - catch (...) {} + catch (const std::ios_base::failure&) {} bool fSkipCheck = false; @@ -943,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; @@ -1011,11 +1013,10 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas Optional<DatabaseFormat> format; if (exists) { - if (ExistsBerkeleyDatabase(path)) { + if (IsBDBFile(BDBDataFile(path))) { format = DatabaseFormat::BERKELEY; } -#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; @@ -1023,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; @@ -1052,15 +1052,31 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set. if (!format && options.require_format) format = options.require_format; + // If the format is not specified or detected, choose the default format based on what is available. We prefer BDB over SQLite for now. + if (!format) { #ifdef USE_SQLITE - if (format && format == DatabaseFormat::SQLITE) { - return MakeSQLiteDatabase(path, options, status, error); + format = DatabaseFormat::SQLITE; +#endif +#ifdef USE_BDB + format = DatabaseFormat::BERKELEY; +#endif } -#else - assert(format != DatabaseFormat::SQLITE); + + if (format == DatabaseFormat::SQLITE) { +#ifdef USE_SQLITE + return MakeSQLiteDatabase(path, options, status, error); #endif + error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", path.string())); + status = DatabaseStatus::FAILED_BAD_FORMAT; + return nullptr; + } +#ifdef USE_BDB return MakeBerkeleyDatabase(path, options, status, error); +#endif + error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", path.string())); + status = DatabaseStatus::FAILED_BAD_FORMAT; + return nullptr; } /** Return object for accessing dummy database with no read/write capabilities. */ @@ -1072,5 +1088,9 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase() /** Return object for accessing temporary in-memory database. */ std::unique_ptr<WalletDatabase> CreateMockWalletDatabase() { +#ifdef USE_BDB return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), ""); +#elif USE_SQLITE + return MakeUnique<SQLiteDatabase>("", "", true); +#endif } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 7f1b86e458..e7b2d7d780 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -8,7 +8,6 @@ #include <amount.h> #include <script/sign.h> -#include <wallet/bdb.h> #include <wallet/db.h> #include <wallet/walletutil.h> #include <key.h> diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 0e18d6a740..fda9025588 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -21,30 +21,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 +82,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; } @@ -110,18 +107,28 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) fs::path path = fs::absolute(name, GetWalletDir()); if (command == "create") { - std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true); + DatabaseOptions options; + options.require_create = true; + if (gArgs.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); + 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); @@ -134,6 +141,10 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) } } return ret; +#else + tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled"); + return false; +#endif } } else { tfm::format(std::cerr, "Invalid command: %s\n", command); diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index 702293e6c7..16ddad3a84 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -7,13 +7,6 @@ #include <logging.h> #include <util/system.h> -bool ExistsBerkeleyDatabase(const fs::path& path); -#ifdef USE_SQLITE -bool ExistsSQLiteDatabase(const fs::path& path); -#else -# define ExistsSQLiteDatabase(path) (false) -#endif - fs::path GetWalletDir() { fs::path path; @@ -36,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; @@ -87,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..d4143ceff4 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -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/warnings.cpp b/src/warnings.cpp index 501bf7e637..1dec663a73 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -14,7 +14,6 @@ static Mutex g_warnings_mutex; static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex); -static bool fLargeWorkForkFound GUARDED_BY(g_warnings_mutex) = false; static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false; void SetMiscWarning(const bilingual_str& warning) @@ -23,18 +22,6 @@ void SetMiscWarning(const bilingual_str& warning) g_misc_warnings = warning; } -void SetfLargeWorkForkFound(bool flag) -{ - LOCK(g_warnings_mutex); - fLargeWorkForkFound = flag; -} - -bool GetfLargeWorkForkFound() -{ - LOCK(g_warnings_mutex); - return fLargeWorkForkFound; -} - void SetfLargeWorkInvalidChainFound(bool flag) { LOCK(g_warnings_mutex); @@ -60,10 +47,7 @@ bilingual_str GetWarnings(bool verbose) warnings_verbose.emplace_back(warnings_concise); } - if (fLargeWorkForkFound) { - warnings_concise = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); - warnings_verbose.emplace_back(warnings_concise); - } else if (fLargeWorkInvalidChainFound) { + if (fLargeWorkInvalidChainFound) { warnings_concise = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); warnings_verbose.emplace_back(warnings_concise); } diff --git a/src/warnings.h b/src/warnings.h index 28546eb753..e87b64a86d 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -11,8 +11,6 @@ struct bilingual_str; void SetMiscWarning(const bilingual_str& warning); -void SetfLargeWorkForkFound(bool flag); -bool GetfLargeWorkForkFound(); void SetfLargeWorkInvalidChainFound(bool flag); /** Format a string that describes several potential problems detected by the core. * @param[in] verbose bool |