diff options
Diffstat (limited to 'src')
47 files changed, 2223 insertions, 1316 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 4b07f06c95..efac2b4695 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -146,6 +146,7 @@ BITCOIN_CORE_H = \ netbase.h \ netmessagemaker.h \ noui.h \ + optional.h \ outputtype.h \ policy/feerate.h \ policy/fees.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 8ce7562434..2e1a2c7766 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -3,7 +3,33 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. bin_PROGRAMS += test/test_bitcoin -noinst_PROGRAMS += test/test_bitcoin_fuzzy + +FUZZ_TARGETS = \ + test/fuzz/address_deserialize \ + test/fuzz/addrman_deserialize \ + test/fuzz/banentry_deserialize \ + test/fuzz/block_deserialize \ + 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/bloomfilter_deserialize \ + test/fuzz/coins_deserialize \ + test/fuzz/diskblockindex_deserialize \ + test/fuzz/inv_deserialize \ + test/fuzz/messageheader_deserialize \ + test/fuzz/netaddr_deserialize \ + test/fuzz/service_deserialize \ + test/fuzz/transaction_deserialize \ + test/fuzz/txoutcompressor_deserialize \ + test/fuzz/txundo_deserialize + +if ENABLE_FUZZ +noinst_PROGRAMS += $(FUZZ_TARGETS:=) +endif + TEST_SRCDIR = test TEST_BINARY=test/test_bitcoin$(EXEEXT) @@ -27,6 +53,10 @@ BITCOIN_TEST_SUITE = \ test/test_bitcoin.h \ test/test_bitcoin.cpp +FUZZ_SUITE = \ + test/fuzz/fuzz.cpp \ + test/fuzz/fuzz.h + # test_bitcoin binary # BITCOIN_TESTS =\ test/arith_uint256_tests.cpp \ @@ -138,28 +168,348 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -s if ENABLE_ZMQ test_test_bitcoin_LDADD += $(ZMQ_LIBS) endif -# - -# test_bitcoin_fuzzy binary # -test_test_bitcoin_fuzzy_SOURCES = test/test_bitcoin_fuzzy.cpp -test_test_bitcoin_fuzzy_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -test_test_bitcoin_fuzzy_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_bitcoin_fuzzy_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) - -test_test_bitcoin_fuzzy_LDADD = \ - $(LIBUNIVALUE) \ - $(LIBBITCOIN_SERVER) \ - $(LIBBITCOIN_COMMON) \ - $(LIBBITCOIN_UTIL) \ - $(LIBBITCOIN_CONSENSUS) \ - $(LIBBITCOIN_CRYPTO) \ - $(LIBBITCOIN_CRYPTO_SSE41) \ - $(LIBBITCOIN_CRYPTO_AVX2) \ - $(LIBBITCOIN_CRYPTO_SHANI) \ - $(LIBSECP256K1) - -test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) -# + +if ENABLE_FUZZ +test_fuzz_block_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_block_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp +test_fuzz_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTRANSACTION_DESERIALIZE=1 +test_fuzz_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_transaction_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_transaction_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_blocklocator_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blocklocator_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_blocklocator_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_blockmerkleroot_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.cpp +test_fuzz_blockmerkleroot_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKMERKLEROOT=1 +test_fuzz_blockmerkleroot_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_blockmerkleroot_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blockmerkleroot_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_blockmerkleroot_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_addrman_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_addrman_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_addrman_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_blockheader_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blockheader_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_blockheader_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_banentry_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_banentry_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_banentry_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_txundo_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_txundo_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_txundo_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_blockundo_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blockundo_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_blockundo_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_coins_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_coins_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_coins_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_netaddr_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_netaddr_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_netaddr_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_service_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_service_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_service_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_messageheader_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_messageheader_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_messageheader_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_address_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_address_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_address_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_inv_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_inv_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_bloomfilter_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_bloomfilter_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_bloomfilter_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_diskblockindex_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_diskblockindex_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_diskblockindex_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_txoutcompressor_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_txoutcompressor_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_txoutcompressor_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_blocktransactions_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blocktransactions_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_blocktransactions_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) + +test_fuzz_blocktransactionsrequest_deserialize_SOURCES = $(FUZZ_SUITE) test/test_bitcoin_fuzzy.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_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_blocktransactionsrequest_deserialize_LDADD = \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CRYPTO_SSE41) \ + $(LIBBITCOIN_CRYPTO_AVX2) \ + $(LIBBITCOIN_CRYPTO_SHANI) \ + $(LIBSECP256K1) +test_fuzz_blocktransactionsrequest_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) +endif # ENABLE_FUZZ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/banman.cpp b/src/banman.cpp index 9933c829c5..47d64a8f31 100644 --- a/src/banman.cpp +++ b/src/banman.cpp @@ -67,14 +67,36 @@ void BanMan::ClearBanned() if (m_client_interface) m_client_interface->BannedListChanged(); } +int BanMan::IsBannedLevel(CNetAddr net_addr) +{ + // Returns the most severe level of banning that applies to this address. + // 0 - Not banned + // 1 - Automatic misbehavior ban + // 2 - Any other ban + int level = 0; + auto current_time = GetTime(); + LOCK(m_cs_banned); + for (const auto& it : m_banned) { + CSubNet sub_net = it.first; + CBanEntry ban_entry = it.second; + + if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) { + if (ban_entry.banReason != BanReasonNodeMisbehaving) return 2; + level = 1; + } + } + return level; +} + bool BanMan::IsBanned(CNetAddr net_addr) { + auto current_time = GetTime(); LOCK(m_cs_banned); for (const auto& it : m_banned) { CSubNet sub_net = it.first; CBanEntry ban_entry = it.second; - if (sub_net.Match(net_addr) && GetTime() < ban_entry.nBanUntil) { + if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) { return true; } } @@ -83,11 +105,12 @@ bool BanMan::IsBanned(CNetAddr net_addr) bool BanMan::IsBanned(CSubNet sub_net) { + auto current_time = GetTime(); LOCK(m_cs_banned); banmap_t::iterator i = m_banned.find(sub_net); if (i != m_banned.end()) { CBanEntry ban_entry = (*i).second; - if (GetTime() < ban_entry.nBanUntil) { + if (current_time < ban_entry.nBanUntil) { return true; } } diff --git a/src/banman.h b/src/banman.h index 69f62be368..a1a00309dd 100644 --- a/src/banman.h +++ b/src/banman.h @@ -42,6 +42,7 @@ public: void Ban(const CNetAddr& net_addr, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false); void Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset = 0, bool since_unix_epoch = false); void ClearBanned(); + int IsBannedLevel(CNetAddr net_addr); bool IsBanned(CNetAddr net_addr); bool IsBanned(CSubNet sub_net); bool Unban(const CNetAddr& net_addr); diff --git a/src/coins.h b/src/coins.h index 94493453f0..d39ebf9062 100644 --- a/src/coins.h +++ b/src/coins.h @@ -298,17 +298,17 @@ private: }; //! Utility function to add all of a transaction's outputs to a cache. -// When check is false, this assumes that overwrites are only possible for coinbase transactions. -// When check is true, the underlying view may be queried to determine whether an addition is -// an overwrite. +//! When check is false, this assumes that overwrites are only possible for coinbase transactions. +//! When check is true, the underlying view may be queried to determine whether an addition is +//! an overwrite. // TODO: pass in a boolean to limit these possible overwrites to known // (pre-BIP34) cases. void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false); //! Utility function to find any unspent output with a given txid. -// This function can be quite expensive because in the event of a transaction -// which is not found in the cache, it can cause up to MAX_OUTPUTS_PER_BLOCK -// lookups to database, so it should be used with care. +//! This function can be quite expensive because in the event of a transaction +//! which is not found in the cache, it can cause up to MAX_OUTPUTS_PER_BLOCK +//! lookups to database, so it should be used with care. const Coin& AccessByTxid(const CCoinsViewCache& cache, const uint256& txid); #endif // BITCOIN_COINS_H diff --git a/src/init.cpp b/src/init.cpp index 77d0505610..019b2e469e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -352,7 +352,7 @@ void SetupServerArgs() gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS); gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS); gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS); - gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in megabytes (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in MiB (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS); gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS); @@ -1061,7 +1061,7 @@ bool AppInitParameterInteraction() if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } - LogPrintf("Prune configured to target %uMiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); + LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); fPruneMode = true; } @@ -1423,12 +1423,12 @@ bool AppInitMain(InitInterfaces& interfaces) nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; LogPrintf("Cache configuration:\n"); - LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { - LogPrintf("* Using %.1fMiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1f MiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024)); } - LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); - LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1f MiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); + LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); bool fLoaded = false; while (!fLoaded && !ShutdownRequested()) { diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 2571a91031..38888be8a3 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -4,7 +4,11 @@ #include <interfaces/chain.h> +#include <chain.h> +#include <chainparams.h> +#include <primitives/block.h> #include <sync.h> +#include <uint256.h> #include <util/system.h> #include <validation.h> @@ -16,6 +20,118 @@ namespace { class LockImpl : public Chain::Lock { + Optional<int> getHeight() override + { + int height = ::chainActive.Height(); + if (height >= 0) { + return height; + } + return nullopt; + } + Optional<int> getBlockHeight(const uint256& hash) override + { + CBlockIndex* block = LookupBlockIndex(hash); + if (block && ::chainActive.Contains(block)) { + return block->nHeight; + } + return nullopt; + } + int getBlockDepth(const uint256& hash) override + { + const Optional<int> tip_height = getHeight(); + const Optional<int> height = getBlockHeight(hash); + return tip_height && height ? *tip_height - *height + 1 : 0; + } + uint256 getBlockHash(int height) override + { + CBlockIndex* block = ::chainActive[height]; + assert(block != nullptr); + return block->GetBlockHash(); + } + int64_t getBlockTime(int height) override + { + CBlockIndex* block = ::chainActive[height]; + assert(block != nullptr); + return block->GetBlockTime(); + } + int64_t getBlockMedianTimePast(int height) override + { + CBlockIndex* block = ::chainActive[height]; + assert(block != nullptr); + return block->GetMedianTimePast(); + } + bool haveBlockOnDisk(int height) override + { + CBlockIndex* block = ::chainActive[height]; + return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0; + } + Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override + { + CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time); + if (block) { + if (hash) *hash = block->GetBlockHash(); + return block->nHeight; + } + return nullopt; + } + Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override + { + // TODO: Could update CChain::FindEarliestAtLeast() to take a height + // parameter and use it with std::lower_bound() to make this + // implementation more efficient and allow combining + // findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one + // method. + for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) { + if (block->GetBlockTime() >= time) { + return block->nHeight; + } + } + return nullopt; + } + Optional<int> findPruned(int start_height, Optional<int> stop_height) override + { + if (::fPruneMode) { + CBlockIndex* block = stop_height ? ::chainActive[*stop_height] : ::chainActive.Tip(); + while (block && block->nHeight >= start_height) { + if ((block->nStatus & BLOCK_HAVE_DATA) == 0) { + return block->nHeight; + } + block = block->pprev; + } + } + return nullopt; + } + Optional<int> findFork(const uint256& hash, Optional<int>* height) override + { + const CBlockIndex* block = LookupBlockIndex(hash); + const CBlockIndex* fork = block ? ::chainActive.FindFork(block) : nullptr; + if (height) { + if (block) { + *height = block->nHeight; + } else { + height->reset(); + } + } + if (fork) { + return fork->nHeight; + } + return nullopt; + } + bool isPotentialTip(const uint256& hash) override + { + if (::chainActive.Tip()->GetBlockHash() == hash) return true; + CBlockIndex* block = LookupBlockIndex(hash); + return block && block->GetAncestor(::chainActive.Height()) == ::chainActive.Tip(); + } + CBlockLocator getLocator() override { return ::chainActive.GetLocator(); } + Optional<int> findLocatorFork(const CBlockLocator& locator) override + { + LockAnnotation lock(::cs_main); + if (CBlockIndex* fork = FindForkInGlobalIndex(::chainActive, locator)) { + return fork->nHeight; + } + return nullopt; + } }; class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection> @@ -35,6 +151,32 @@ public: return std::move(result); } std::unique_ptr<Chain::Lock> assumeLocked() override { return MakeUnique<LockImpl>(); } + bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override + { + CBlockIndex* index; + { + LOCK(cs_main); + index = LookupBlockIndex(hash); + if (!index) { + return false; + } + if (time) { + *time = index->GetBlockTime(); + } + if (time_max) { + *time_max = index->GetBlockTimeMax(); + } + } + if (block && !ReadBlockFromDisk(*block, index, Params().GetConsensus())) { + block->SetNull(); + } + return true; + } + double guessVerificationProgress(const uint256& block_hash) override + { + LOCK(cs_main); + return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash)); + } }; } // namespace diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index fe5658de4b..735d5b60df 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -5,11 +5,17 @@ #ifndef BITCOIN_INTERFACES_CHAIN_H #define BITCOIN_INTERFACES_CHAIN_H +#include <optional.h> + #include <memory> +#include <stdint.h> #include <string> #include <vector> +class CBlock; class CScheduler; +class uint256; +struct CBlockLocator; namespace interfaces { @@ -28,6 +34,74 @@ public: { public: virtual ~Lock() {} + + //! Get current chain height, not including genesis block (returns 0 if + //! chain only contains genesis block, nullopt if chain does not contain + //! any blocks). + virtual Optional<int> getHeight() = 0; + + //! 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 depth. Returns 1 for chain tip, 2 for preceding block, and + //! so on. Returns 0 for a block not included in the current chain. + virtual int getBlockDepth(const uint256& hash) = 0; + + //! Get block hash. Height must be valid or this function will abort. + virtual uint256 getBlockHash(int height) = 0; + + //! Get block time. Height must be valid or this function will abort. + virtual int64_t getBlockTime(int height) = 0; + + //! Get block median time past. Height must be valid or this function + //! will abort. + virtual int64_t getBlockMedianTimePast(int height) = 0; + + //! Check that the block is available on disk (i.e. has not been + //! 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, or nullopt if there is no block with + //! a high enough timestamp. 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> findFirstBlockWithTime(int64_t time, uint256* hash) = 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 such block. + //! + //! Calling this with height 0 is equivalent to calling + //! findFirstBlockWithTime, but less efficient because it requires a + //! linear instead of a binary search. + virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0; + + //! Return height of last block in the specified range which is pruned, or + //! nullopt if no block in the range is pruned. Range is inclusive. + virtual Optional<int> findPruned(int start_height = 0, Optional<int> stop_height = nullopt) = 0; + + //! Return height of the highest block on the chain that is an ancestor + //! of the specified block, or nullopt if no common ancestor is found. + //! Also return the height of the specified block as an optional output + //! parameter (to avoid the cost of a second hash lookup in case this + //! information is desired). + virtual Optional<int> findFork(const uint256& hash, Optional<int>* height) = 0; + + //! Return true if block hash points to the current chain tip, or to a + //! possible descendant of the current chain tip that isn't currently + //! connected. + virtual bool isPotentialTip(const uint256& hash) = 0; + + //! Get locator for the current chain tip. + virtual CBlockLocator getLocator() = 0; + + //! Return height of the latest block common to locator and chain, which + //! is guaranteed to be an ancestor of the block used to create the + //! locator. + virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0; }; //! Return Lock interface. Chain is locked when this is called, and @@ -38,6 +112,21 @@ public: //! method is temporary and is only used in a few places to avoid changing //! behavior while code is transitioned to use the Chain::Lock interface. virtual std::unique_ptr<Lock> assumeLocked() = 0; + + //! Return whether node has the block and optionally return block metadata + //! or contents. + //! + //! If a block pointer is provided to retrieve the block contents, and the + //! block exists but doesn't have data (for example due to pruning), the + //! block will be empty and all fields set to null. + virtual bool findBlock(const uint256& hash, + CBlock* block = nullptr, + int64_t* time = nullptr, + int64_t* max_time = nullptr) = 0; + + //! Estimate fraction of total transactions verified if blocks up to + //! the specified block hash are verified. + virtual double guessVerificationProgress(const uint256& block_hash) = 0; }; //! Interface to let node manage chain clients (wallets, or maybe tools for diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 8db34ed759..62ede0a95a 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -333,8 +333,13 @@ public: if (mi == m_wallet.mapWallet.end()) { return false; } - num_blocks = ::chainActive.Height(); - block_time = ::chainActive.Tip()->GetBlockTime(); + if (Optional<int> height = locked_chain->getHeight()) { + num_blocks = *height; + block_time = locked_chain->getBlockTime(*height); + } else { + num_blocks = -1; + block_time = -1; + } tx_status = MakeWalletTxStatus(*locked_chain, mi->second); return true; } @@ -348,7 +353,7 @@ public: LOCK(m_wallet.cs_wallet); auto mi = m_wallet.mapWallet.find(txid); if (mi != m_wallet.mapWallet.end()) { - num_blocks = ::chainActive.Height(); + num_blocks = locked_chain->getHeight().value_or(-1); in_mempool = mi->second.InMempool(); order_form = mi->second.vOrderForm; tx_status = MakeWalletTxStatus(*locked_chain, mi->second); @@ -379,7 +384,7 @@ public: return false; } balances = getBalances(); - num_blocks = ::chainActive.Height(); + num_blocks = locked_chain->getHeight().value_or(-1); return true; } CAmount getBalance() override { return m_wallet.GetBalance(); } @@ -488,6 +493,10 @@ public: { return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn)); } + std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) override + { + return MakeHandler(m_wallet.NotifyCanGetAddressesChanged.connect(fn)); + } std::shared_ptr<CWallet> m_shared_wallet; CWallet& m_wallet; diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index da60684a4f..72c64ded01 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -271,6 +271,10 @@ public: //! Register handler for watchonly changed messages. using WatchOnlyChangedFn = std::function<void(bool have_watch_only)>; virtual std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) = 0; + + //! Register handler for keypool changed messages. + using CanGetAddressesChangedFn = std::function<void()>; + virtual std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) = 0; }; //! Tracking object returned by CreateTransaction and passed to CommitTransaction. diff --git a/src/leveldb/db/c.cc b/src/leveldb/db/c.cc index 08ff0ad90a..b23e3dcc9d 100644 --- a/src/leveldb/db/c.cc +++ b/src/leveldb/db/c.cc @@ -5,7 +5,9 @@ #include "leveldb/c.h" #include <stdlib.h> +#ifndef WIN32 #include <unistd.h> +#endif #include "leveldb/cache.h" #include "leveldb/comparator.h" #include "leveldb/db.h" diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h index e8bf46ef27..989c15cd91 100644 --- a/src/leveldb/port/port_win.h +++ b/src/leveldb/port/port_win.h @@ -32,9 +32,16 @@ #define STORAGE_LEVELDB_PORT_PORT_WIN_H_ #ifdef _MSC_VER +#if !(_MSC_VER >= 1900) #define snprintf _snprintf +#endif #define close _close #define fread_unlocked _fread_nolock +#ifdef _WIN64 +#define ssize_t int64_t +#else +#define ssize_t int32_t +#endif #endif #include <string> diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc index 81380216bb..830332abe9 100644 --- a/src/leveldb/util/env_win.cc +++ b/src/leveldb/util/env_win.cc @@ -203,24 +203,16 @@ public: void ToWidePath(const std::string& value, std::wstring& target) { wchar_t buffer[MAX_PATH]; - MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, buffer, MAX_PATH); target = buffer; } void ToNarrowPath(const std::wstring& value, std::string& target) { char buffer[MAX_PATH]; - WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); target = buffer; } -std::string GetCurrentDir() -{ - CHAR path[MAX_PATH]; - ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH); - *strrchr(path,'\\') = 0; - return std::string(path); -} - std::wstring GetCurrentDirW() { WCHAR path[MAX_PATH]; @@ -229,6 +221,13 @@ std::wstring GetCurrentDirW() return std::wstring(path); } +std::string GetCurrentDir() +{ + std::string path; + ToNarrowPath(GetCurrentDirW(), path); + return path; +} + std::string& ModifyPath(std::string& path) { if(path[0] == '/' || path[0] == '\\'){ @@ -764,14 +763,16 @@ uint64_t Win32Env::NowMicros() static Status CreateDirInner( const std::string& dirname ) { Status sRet; - DWORD attr = ::GetFileAttributes(dirname.c_str()); + std::wstring dirnameW; + ToWidePath(dirname, dirnameW); + DWORD attr = ::GetFileAttributesW(dirnameW.c_str()); if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: std::size_t slash = dirname.find_last_of("\\"); if (slash != std::string::npos){ sRet = CreateDirInner(dirname.substr(0, slash)); if (!sRet.ok()) return sRet; } - BOOL result = ::CreateDirectory(dirname.c_str(), NULL); + BOOL result = ::CreateDirectoryW(dirnameW.c_str(), NULL); if (result == FALSE) { sRet = Status::IOError(dirname, "Could not create directory."); return sRet; diff --git a/src/net.cpp b/src/net.cpp index 0490ccd6db..be249b4466 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -764,6 +764,7 @@ struct NodeEvictionCandidate bool fBloomFilter; CAddress addr; uint64_t nKeyedNetGroup; + bool prefer_evict; }; static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) @@ -832,7 +833,8 @@ bool CConnman::AttemptToEvictConnection() NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, HasAllDesirableServiceFlags(node->nServices), - node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup}; + node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup, + node->m_prefer_evict}; vEvictionCandidates.push_back(candidate); } } @@ -857,6 +859,14 @@ bool CConnman::AttemptToEvictConnection() if (vEvictionCandidates.empty()) return false; + // 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) + // then we probably don't want to evict it no matter what. + if (std::any_of(vEvictionCandidates.begin(),vEvictionCandidates.end(),[](NodeEvictionCandidate const &n){return n.prefer_evict;})) { + vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.begin(),vEvictionCandidates.end(), + [](NodeEvictionCandidate const &n){return !n.prefer_evict;}),vEvictionCandidates.end()); + } + // Identify the network group with the most connections and youngest member. // (vEvictionCandidates is already sorted by reverse connect time) uint64_t naMostConnections; @@ -937,7 +947,11 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { // on all platforms. Set it again here just to be sure. SetSocketNoDelay(hSocket); - if (m_banman && m_banman->IsBanned(addr) && !whitelisted) + int bannedlevel = m_banman ? m_banman->IsBannedLevel(addr) : 0; + + // Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept + // if the only banning reason was an automatic misbehavior ban. + if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0)) { LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); @@ -961,6 +975,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; + pnode->m_prefer_evict = bannedlevel > 0; m_msgproc->InitializeNode(pnode); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); @@ -273,17 +273,17 @@ public: void SetMaxOutboundTimeframe(uint64_t timeframe); uint64_t GetMaxOutboundTimeframe(); - //!check if the outbound target is reached - // if param historicalBlockServingLimit is set true, the function will - // response true if the limit for serving historical blocks has been reached + //! check if the outbound target is reached + //! if param historicalBlockServingLimit is set true, the function will + //! response true if the limit for serving historical blocks has been reached bool OutboundTargetReached(bool historicalBlockServingLimit); - //!response the bytes left in the current max outbound cycle - // in case of no limit, it will always response 0 + //! response the bytes left in the current max outbound cycle + //! in case of no limit, it will always response 0 uint64_t GetOutboundTargetBytesLeft(); - //!response the time in second left in the current max outbound cycle - // in case of no limit, it will always response 0 + //! response the time in second left in the current max outbound cycle + //! in case of no limit, it will always response 0 uint64_t GetMaxOutboundTimeLeftInCycle(); uint64_t GetTotalBytesRecv(); @@ -651,6 +651,7 @@ public: // the network or wire types and the cleaned string used when displayed or logged. std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer); CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer + bool m_prefer_evict{false}; // This peer is preferred for eviction. bool fWhitelisted{false}; // This peer can bypass DoS banning. bool fFeeler{false}; // If true this node is being used as a short lived feeler. bool fOneShot{false}; diff --git a/src/optional.h b/src/optional.h new file mode 100644 index 0000000000..95a3b24d0a --- /dev/null +++ b/src/optional.h @@ -0,0 +1,26 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_OPTIONAL_H +#define BITCOIN_OPTIONAL_H + +#include <utility> + +#include <boost/optional.hpp> + +//! Substitute for C++17 std::optional +template <typename T> +using Optional = boost::optional<T>; + +//! Substitute for C++17 std::make_optional +template <typename T> +Optional<T> MakeOptional(bool condition, T&& value) +{ + return boost::make_optional(condition, std::forward<T>(value)); +} + +//! Substitute for C++17 std::nullopt +static auto& nullopt = boost::none; + +#endif // BITCOIN_OPTIONAL_H diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 507d195b72..240a7a7e92 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -121,7 +121,7 @@ <item> <widget class="QLabel" name="databaseCacheUnitLabel"> <property name="text"> - <string>MB</string> + <string>MiB</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 4d6006c582..736ff13a4a 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -52,4 +52,7 @@ static const int MAX_URI_LENGTH = 255; #define QAPP_APP_NAME_TESTNET "Bitcoin-Qt-testnet" #define QAPP_APP_NAME_REGTEST "Bitcoin-Qt-regtest" +/* One gigabyte (GB) in bytes */ +static constexpr uint64_t GB_BYTES{1000000000}; + #endif // BITCOIN_QT_GUICONSTANTS_H diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 69972fce3b..499af9fa07 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -10,6 +10,7 @@ #include <qt/intro.h> #include <qt/forms/ui_intro.h> +#include <qt/guiconstants.h> #include <qt/guiutil.h> #include <interfaces/node.h> @@ -21,7 +22,6 @@ #include <cmath> -static const uint64_t GB_BYTES = 1000000000LL; /* Total required space (in GB) depending on user choice (prune, not prune) */ static uint64_t requiredSpace; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 849bc2e477..9094aeff56 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -10,6 +10,7 @@ #include <qt/forms/ui_optionsdialog.h> #include <qt/bitcoinunits.h> +#include <qt/guiconstants.h> #include <qt/guiutil.h> #include <qt/optionsmodel.h> @@ -37,10 +38,6 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : /* Main elements init */ ui->databaseCache->setMinimum(nMinDbCache); ui->databaseCache->setMaximum(nMaxDbCache); - static const uint64_t GiB = 1024 * 1024 * 1024; - static const uint64_t nMinDiskSpace = MIN_DISK_SPACE_FOR_BLOCK_FILES / GiB + - (MIN_DISK_SPACE_FOR_BLOCK_FILES % GiB) ? 1 : 0; - ui->pruneSize->setMinimum(nMinDiskSpace); ui->threadsScriptVerif->setMinimum(-GetNumCores()); ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS); ui->pruneWarning->setVisible(false); @@ -167,6 +164,10 @@ void OptionsDialog::setModel(OptionsModel *_model) mapper->toFirst(); updateDefaultProxyNets(); + + // Prune values are in GB to be consistent with intro.cpp + static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0; + ui->pruneSize->setRange(nMinDiskSpace, _model->node().getAssumedBlockchainSize()); } /* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index d04a2cf862..62496a57f4 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -9,6 +9,7 @@ #include <qt/optionsmodel.h> #include <qt/bitcoinunits.h> +#include <qt/guiconstants.h> #include <qt/guiutil.h> #include <interfaces/node.h> @@ -92,10 +93,10 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("bPrune", false); if (!settings.contains("nPruneSize")) settings.setValue("nPruneSize", 2); - // Convert prune size to MB: - const uint64_t nPruneSizeMB = settings.value("nPruneSize").toInt() * 1000; - if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool() ? std::to_string(nPruneSizeMB) : "0")) { - addOverriddenOption("-prune"); + // Convert prune size from GB to MiB: + const uint64_t nPruneSizeMiB = (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20; + if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool() ? std::to_string(nPruneSizeMiB) : "0")) { + addOverriddenOption("-prune"); } if (!settings.contains("nDatabaseCache")) diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index bc96b5a6f7..22a79a12bb 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -100,8 +100,13 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) ui->useBech32->setCheckState(Qt::Unchecked); } - // eventually disable the main receive button if private key operations are disabled - ui->receiveButton->setEnabled(!model->privateKeysDisabled()); + // Set the button to be enabled or disabled based on whether the wallet can give out new addresses. + ui->receiveButton->setEnabled(model->canGetAddresses()); + + // Enable/disable the receive button if the wallet is now able/unable to give out new addresses. + connect(model, &WalletModel::canGetAddressesChanged, [this] { + ui->receiveButton->setEnabled(model->canGetAddresses()); + }); } } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 610d83acb6..ee84da0cdf 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -1,6 +1,7 @@ #include <qt/test/wallettests.h> #include <qt/test/util.h> +#include <init.h> #include <interfaces/chain.h> #include <interfaces/node.h> #include <base58.h> @@ -146,13 +147,10 @@ void TestGUI() auto locked_chain = wallet->chain().lock(); WalletRescanReserver reserver(wallet.get()); reserver.reserve(); - const CBlockIndex* const null_block = nullptr; - const CBlockIndex *stop_block, *failed_block; - QCOMPARE( - wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block, true /* fUpdate */), - CWallet::ScanResult::SUCCESS); - QCOMPARE(stop_block, chainActive.Tip()); - QCOMPARE(failed_block, null_block); + CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */); + QCOMPARE(result.status, CWallet::ScanResult::SUCCESS); + QCOMPARE(result.stop_block, chainActive.Tip()->GetBlockHash()); + QVERIFY(result.failed_block.IsNull()); } wallet->SetBroadcastTransactions(true); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f139152042..2a9144bec9 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -423,6 +423,11 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly Q_ARG(bool, fHaveWatchonly)); } +static void NotifyCanGetAddressesChanged(WalletModel* walletmodel) +{ + QMetaObject::invokeMethod(walletmodel, "canGetAddressesChanged"); +} + void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet @@ -432,6 +437,7 @@ void WalletModel::subscribeToCoreSignals() m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2)); m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)); m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1)); + m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(boost::bind(NotifyCanGetAddressesChanged, this)); } void WalletModel::unsubscribeFromCoreSignals() @@ -443,6 +449,7 @@ void WalletModel::unsubscribeFromCoreSignals() m_handler_transaction_changed->disconnect(); m_handler_show_progress->disconnect(); m_handler_watch_only_changed->disconnect(); + m_handler_can_get_addrs_changed->disconnect(); } // WalletModel::UnlockContext implementation @@ -571,6 +578,16 @@ bool WalletModel::privateKeysDisabled() const return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); } +bool WalletModel::canGetAddresses() const +{ + // The wallet can provide a fresh address if: + // * hdEnabled(): an HD seed is present; or + // * it is a legacy wallet, because: + // * !hdEnabled(): an HD seed is not present; and + // * !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS): private keys have not been disabled (which results in hdEnabled() == true) + return m_wallet->hdEnabled() || (!m_wallet->hdEnabled() && !m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); +} + QString WalletModel::getWalletName() const { return QString::fromStdString(m_wallet->getWalletName()); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index c3c8f36909..b123befbb4 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -214,6 +214,7 @@ public: static bool isWalletEnabled(); bool privateKeysDisabled() const; + bool canGetAddresses() const; interfaces::Node& node() const { return m_node; } interfaces::Wallet& wallet() const { return *m_wallet; } @@ -232,6 +233,7 @@ private: std::unique_ptr<interfaces::Handler> m_handler_transaction_changed; std::unique_ptr<interfaces::Handler> m_handler_show_progress; std::unique_ptr<interfaces::Handler> m_handler_watch_only_changed; + std::unique_ptr<interfaces::Handler> m_handler_can_get_addrs_changed; interfaces::Node& m_node; bool fHaveWatchOnly; @@ -283,6 +285,9 @@ Q_SIGNALS: // Signal that wallet is about to be removed void unload(); + // Notify that there are now keys in the keypool + void canGetAddressesChanged(); + public Q_SLOTS: /* Wallet status might have changed */ void updateStatus(); diff --git a/src/rest.cpp b/src/rest.cpp index c7a627d14e..326f7ae1d2 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -352,7 +352,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) CTransactionRef tx; uint256 hashBlock = uint256(); - if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock)) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); switch (rf) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 315f69d46b..fa0b62ec48 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -162,14 +162,16 @@ static UniValue getblockcount(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getblockcount", - "\nReturns the number of blocks in the longest blockchain.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns the number of blocks in the longest blockchain.\n", + {}, + RPCResult{ "n (numeric) The current block count\n" - "\nExamples:\n" - + HelpExampleCli("getblockcount", "") + }, + RPCExamples{ + HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "") - ); + }, + }.ToString()); LOCK(cs_main); return chainActive.Height(); @@ -180,14 +182,16 @@ static UniValue getbestblockhash(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getbestblockhash", - "\nReturns the hash of the best (tip) block in the longest blockchain.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns the hash of the best (tip) block in the longest blockchain.\n", + {}, + RPCResult{ "\"hex\" (string) the block hash, hex-encoded\n" - "\nExamples:\n" - + HelpExampleCli("getbestblockhash", "") + }, + RPCExamples{ + HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "") - ); + }, + }.ToString()); LOCK(cs_main); return chainActive.Tip()->GetBlockHash().GetHex(); @@ -212,17 +216,18 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) "\nReturns the current block on timeout or exit.\n", { {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("waitfornewblock", "1000") + }, + RPCExamples{ + HelpExampleCli("waitfornewblock", "1000") + HelpExampleRpc("waitfornewblock", "1000") - ); + }, + }.ToString()); int timeout = 0; if (!request.params[0].isNull()) timeout = request.params[0].get_int(); @@ -253,17 +258,18 @@ static UniValue waitforblock(const JSONRPCRequest& request) { {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "Block hash to wait for."}, {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") + }, + RPCExamples{ + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") - ); + }, + }.ToString()); int timeout = 0; uint256 hash(ParseHashV(request.params[0], "blockhash")); @@ -298,17 +304,18 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) { {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Block height to wait for."}, {"timeout", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("waitforblockheight", "\"100\", 1000") + }, + RPCExamples{ + HelpExampleCli("waitforblockheight", "\"100\", 1000") + HelpExampleRpc("waitforblockheight", "\"100\", 1000") - ); + }, + }.ToString()); int timeout = 0; int height = request.params[0].get_int(); @@ -336,12 +343,14 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 0) { throw std::runtime_error( RPCHelpMan{"syncwithvalidationinterfacequeue", - "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n", {}} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("syncwithvalidationinterfacequeue","") + "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n", + {}, + RPCResults{}, + RPCExamples{ + HelpExampleCli("syncwithvalidationinterfacequeue","") + HelpExampleRpc("syncwithvalidationinterfacequeue","") - ); + }, + }.ToString()); } SyncWithValidationInterfaceQueue(); return NullUniValue; @@ -352,14 +361,16 @@ static UniValue getdifficulty(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getdifficulty", - "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", + {}, + RPCResult{ "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" - "\nExamples:\n" - + HelpExampleCli("getdifficulty", "") + }, + RPCExamples{ + HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "") - ); + }, + }.ToString()); LOCK(cs_main); return GetDifficulty(chainActive.Tip()); @@ -491,9 +502,8 @@ static UniValue getrawmempool(const JSONRPCRequest& request) "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n", { {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"}, - }} - .ToString() + - "\nResult: (for verbose = false):\n" + }, + RPCResult{"for verbose = false", "[ (json array of string)\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" @@ -504,10 +514,12 @@ static UniValue getrawmempool(const JSONRPCRequest& request) + EntryDescriptionString() + " }, ...\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getrawmempool", "true") + }, + RPCExamples{ + HelpExampleCli("getrawmempool", "true") + HelpExampleRpc("getrawmempool", "true") - ); + }, + }.ToString()); bool fVerbose = false; if (!request.params[0].isNull()) @@ -525,23 +537,27 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) { {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"}, {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"}, - }} - .ToString() + - "\nResult (for verbose = false):\n" + }, + { + RPCResult{"for verbose = false", "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n" " ,...\n" "]\n" - "\nResult (for verbose = true):\n" + }, + RPCResult{"for verbose = true", "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempoolancestors", "\"mytxid\"") + }, + }, + RPCExamples{ + HelpExampleCli("getmempoolancestors", "\"mytxid\"") + HelpExampleRpc("getmempoolancestors", "\"mytxid\"") - ); + }, + }.ToString()); } bool fVerbose = false; @@ -591,23 +607,27 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) { {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"}, {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "True for a json object, false for array of transaction ids"}, - }} - .ToString() + - "\nResult (for verbose = false):\n" + }, + { + RPCResult{"for verbose = false", "[ (json array of strings)\n" " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n" " ,...\n" "]\n" - "\nResult (for verbose = true):\n" + }, + RPCResult{"for verbose = true", "{ (json object)\n" " \"transactionid\" : { (json object)\n" + EntryDescriptionString() + " }, ...\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempooldescendants", "\"mytxid\"") + }, + }, + RPCExamples{ + HelpExampleCli("getmempooldescendants", "\"mytxid\"") + HelpExampleRpc("getmempooldescendants", "\"mytxid\"") - ); + }, + }.ToString()); } bool fVerbose = false; @@ -656,16 +676,17 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) "\nReturns mempool data for given transaction\n", { {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id (must be in mempool)"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object)\n" + EntryDescriptionString() + "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempoolentry", "\"mytxid\"") + }, + RPCExamples{ + HelpExampleCli("getmempoolentry", "\"mytxid\"") + HelpExampleRpc("getmempoolentry", "\"mytxid\"") - ); + }, + }.ToString()); } uint256 hash = ParseHashV(request.params[0], "parameter 1"); @@ -691,14 +712,15 @@ static UniValue getblockhash(const JSONRPCRequest& request) "\nReturns hash of block in best-block-chain at height provided.\n", { {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The height index"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"hash\" (string) The block hash\n" - "\nExamples:\n" - + HelpExampleCli("getblockhash", "1000") + }, + RPCExamples{ + HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000") - ); + }, + }.ToString()); LOCK(cs_main); @@ -720,9 +742,9 @@ static UniValue getblockheader(const JSONRPCRequest& request) { {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"}, {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "true for a json object, false for the hex-encoded data"}, - }} - .ToString() + - "\nResult (for verbose = true):\n" + }, + { + RPCResult{"for verbose = true", "{\n" " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" @@ -740,12 +762,16 @@ static UniValue getblockheader(const JSONRPCRequest& request) " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" "}\n" - "\nResult (for verbose=false):\n" + }, + RPCResult{"for verbose=false", "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nExamples:\n" - + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + }, + }, + RPCExamples{ + HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - ); + }, + }.ToString()); uint256 hash(ParseHashV(request.params[0], "hash")); @@ -806,11 +832,12 @@ static UniValue getblock(const JSONRPCRequest& request) { {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The block hash"}, {"verbosity", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"}, - }} - .ToString() + - "\nResult (for verbosity = 0):\n" + }, + { + RPCResult{"for verbosity = 0", "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - "\nResult (for verbosity = 1):\n" + }, + RPCResult{"for verbosity = 1", "{\n" " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" @@ -835,7 +862,8 @@ static UniValue getblock(const JSONRPCRequest& request) " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" "}\n" - "\nResult (for verbosity = 2):\n" + }, + RPCResult{"for verbosity = 2", "{\n" " ..., Same output as verbosity = 1.\n" " \"tx\" : [ (array of Objects) The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result.\n" @@ -843,10 +871,13 @@ static UniValue getblock(const JSONRPCRequest& request) " ],\n" " ,... Same output as verbosity = 1.\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + }, + }, + RPCExamples{ + HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") - ); + }, + }.ToString()); LOCK(cs_main); @@ -957,13 +988,15 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) { {"height", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n" " to prune blocks whose block time is at least 2 hours older than the provided timestamp."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "n (numeric) Height of the last block pruned.\n" - "\nExamples:\n" - + HelpExampleCli("pruneblockchain", "1000") - + HelpExampleRpc("pruneblockchain", "1000")); + }, + RPCExamples{ + HelpExampleCli("pruneblockchain", "1000") + + HelpExampleRpc("pruneblockchain", "1000") + }, + }.ToString()); if (!fPruneMode) throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode."); @@ -1007,9 +1040,8 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) RPCHelpMan{"gettxoutsetinfo", "\nReturns statistics about the unspent transaction output set.\n" "Note this call may take some time.\n", - {}} - .ToString() + - "\nResult:\n" + {}, + RPCResult{ "{\n" " \"height\":n, (numeric) The current block height (index)\n" " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n" @@ -1020,10 +1052,12 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) " \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n" " \"total_amount\": x.xxx (numeric) The total amount\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("gettxoutsetinfo", "") + }, + RPCExamples{ + HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "") - ); + }, + }.ToString()); UniValue ret(UniValue::VOBJ); @@ -1054,9 +1088,8 @@ UniValue gettxout(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"}, {"n", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "vout number"}, {"include_mempool", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" @@ -1073,15 +1106,16 @@ UniValue gettxout(const JSONRPCRequest& request) " },\n" " \"coinbase\" : true|false (boolean) Coinbase or not\n" "}\n" - - "\nExamples:\n" + }, + RPCExamples{ "\nGet unspent transactions\n" + HelpExampleCli("listunspent", "") + "\nView the details\n" + HelpExampleCli("gettxout", "\"txid\" 1") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("gettxout", "\"txid\", 1") - ); + }, + }.ToString()); LOCK(cs_main); @@ -1134,14 +1168,15 @@ static UniValue verifychain(const JSONRPCRequest& request) { {"checklevel", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, range=0-4", nCheckLevel), "How thorough the block verification is."}, {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "true|false (boolean) Verified or not\n" - "\nExamples:\n" - + HelpExampleCli("verifychain", "") + }, + RPCExamples{ + HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "") - ); + }, + }.ToString()); LOCK(cs_main); @@ -1229,9 +1264,9 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getblockchaininfo", - "Returns an object containing various state info regarding blockchain processing.\n", {}} - .ToString() + - "\nResult:\n" + "Returns an object containing various state info regarding blockchain processing.\n", + {}, + RPCResult{ "{\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" @@ -1274,10 +1309,12 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " }\n" " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getblockchaininfo", "") + }, + RPCExamples{ + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "") - ); + }, + }.ToString()); LOCK(cs_main); @@ -1349,9 +1386,8 @@ static UniValue getchaintips(const JSONRPCRequest& request) RPCHelpMan{"getchaintips", "Return information about all known tips in the block tree," " including the main chain as well as orphaned branches.\n", - {}} - .ToString() + - "\nResult:\n" + {}, + RPCResult{ "[\n" " {\n" " \"height\": xxxx, (numeric) height of the chain tip\n" @@ -1372,10 +1408,12 @@ static UniValue getchaintips(const JSONRPCRequest& request) "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" - "\nExamples:\n" - + HelpExampleCli("getchaintips", "") + }, + RPCExamples{ + HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "") - ); + }, + }.ToString()); LOCK(cs_main); @@ -1466,9 +1504,9 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getmempoolinfo", - "\nReturns details on the active state of the TX memory pool.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns details on the active state of the TX memory pool.\n", + {}, + RPCResult{ "{\n" " \"size\": xxxxx, (numeric) Current tx count\n" " \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n" @@ -1477,10 +1515,12 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n" " \"minrelaytxfee\": xxxxx (numeric) Current minimum relay fee for transactions\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getmempoolinfo", "") + }, + RPCExamples{ + HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "") - ); + }, + }.ToString()); return mempoolInfoToJSON(); } @@ -1495,13 +1535,13 @@ static UniValue preciousblock(const JSONRPCRequest& request) "\nThe effects of preciousblock are not retained across restarts.\n", { {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as precious"}, - }} - .ToString() + - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("preciousblock", "\"blockhash\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("preciousblock", "\"blockhash\"") + HelpExampleRpc("preciousblock", "\"blockhash\"") - ); + }, + }.ToString()); uint256 hash(ParseHashV(request.params[0], "blockhash")); CBlockIndex* pblockindex; @@ -1532,13 +1572,13 @@ static UniValue invalidateblock(const JSONRPCRequest& request) "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n", { {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to mark as invalid"}, - }} - .ToString() + - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("invalidateblock", "\"blockhash\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("invalidateblock", "\"blockhash\"") + HelpExampleRpc("invalidateblock", "\"blockhash\"") - ); + }, + }.ToString()); uint256 hash(ParseHashV(request.params[0], "blockhash")); CValidationState state; @@ -1573,13 +1613,13 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) "This can be used to undo the effects of invalidateblock.\n", { {"blockhash", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hash of the block to reconsider"}, - }} - .ToString() + - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("reconsiderblock", "\"blockhash\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("reconsiderblock", "\"blockhash\"") + HelpExampleRpc("reconsiderblock", "\"blockhash\"") - ); + }, + }.ToString()); uint256 hash(ParseHashV(request.params[0], "blockhash")); @@ -1612,9 +1652,8 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) { {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "one month", "Size of the window in number of blocks"}, {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "chain tip", "The hash of the block that ends the window."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" @@ -1624,10 +1663,12 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getchaintxstats", "") + }, + RPCExamples{ + HelpExampleCli("getchaintxstats", "") + HelpExampleRpc("getchaintxstats", "2016") - ); + }, + }.ToString()); const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month @@ -1751,9 +1792,8 @@ static UniValue getblockstats(const JSONRPCRequest& request) {"time", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Selected statistic"}, }, "stats"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object)\n" " \"avgfee\": xxxxx, (numeric) Average fee in the block\n" " \"avgfeerate\": xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n" @@ -1791,10 +1831,12 @@ static UniValue getblockstats(const JSONRPCRequest& request) " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n" " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") + }, + RPCExamples{ + HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") + HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") - ); + }, + }.ToString()); } LOCK(cs_main); @@ -1918,7 +1960,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) for (const CTxIn& in : tx->vin) { CTransactionRef tx_in; uint256 hashBlock; - if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock, false)) { + if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock)) { throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)")); } @@ -2006,12 +2048,14 @@ static UniValue savemempool(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( RPCHelpMan{"savemempool", - "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n", {}} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("savemempool", "") + "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n", + {}, + RPCResults{}, + RPCExamples{ + HelpExampleCli("savemempool", "") + HelpExampleRpc("savemempool", "") - ); + }, + }.ToString()); } if (!g_is_mempool_loaded) { @@ -2120,9 +2164,8 @@ UniValue scantxoutset(const JSONRPCRequest& request) }, }, "[scanobjects,...]"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"unspents\": [\n" " {\n" @@ -2136,6 +2179,9 @@ UniValue scantxoutset(const JSONRPCRequest& request) " ,...], \n" " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" "]\n" + }, + RPCExamples{""}, + }.ToString() ); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2fd6f99be5..200dfa107b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -94,14 +94,15 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request) { {"nblocks", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "120", "The number of blocks, or -1 for blocks since last difficulty change."}, {"height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "-1", "To estimate at the time of the given height."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "x (numeric) Hashes per second estimated\n" - "\nExamples:\n" - + HelpExampleCli("getnetworkhashps", "") + }, + RPCExamples{ + HelpExampleCli("getnetworkhashps", "") + HelpExampleRpc("getnetworkhashps", "") - ); + }, + }.ToString()); LOCK(cs_main); return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1); @@ -165,16 +166,17 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."}, {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The address to send the newly generated bitcoin to."}, {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[ blockhashes ] (array) hashes of blocks generated\n" - "\nExamples:\n" + }, + RPCExamples{ "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") + "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n" + HelpExampleCli("getnewaddress", "") - ); + }, + }.ToString()); int nGenerate = request.params[0].get_int(); uint64_t nMaxTries = 1000000; @@ -198,9 +200,9 @@ static UniValue getmininginfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getmininginfo", - "\nReturns a json object containing mining-related information.", {}} - .ToString() + - "\nResult:\n" + "\nReturns a json object containing mining-related information.", + {}, + RPCResult{ "{\n" " \"blocks\": nnn, (numeric) The current block\n" " \"currentblockweight\": nnn, (numeric) The last block weight\n" @@ -211,10 +213,12 @@ static UniValue getmininginfo(const JSONRPCRequest& request) " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" " \"warnings\": \"...\" (string) any network and blockchain warnings\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getmininginfo", "") + }, + RPCExamples{ + HelpExampleCli("getmininginfo", "") + HelpExampleRpc("getmininginfo", "") - ); + }, + }.ToString()); LOCK(cs_main); @@ -247,14 +251,15 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n" " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" " considers the transaction as it would have paid a higher (or lower) fee."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "true (boolean) Returns true\n" - "\nExamples:\n" - + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + }, + RPCExamples{ + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000") - ); + }, + }.ToString()); LOCK(cs_main); @@ -326,9 +331,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) }, }, "\"template_request\""}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"version\" : n, (numeric) The preferred block version\n" " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" @@ -372,11 +376,12 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n" " \"height\" : n (numeric) The height of the next block\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}") + }, + RPCExamples{ + HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}") + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") - ); + }, + }.ToString()); LOCK(cs_main); @@ -713,13 +718,13 @@ static UniValue submitblock(const JSONRPCRequest& request) { {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block data to submit"}, {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."}, - }} - .ToString() + - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("submitblock", "\"mydata\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\"") - ); + }, + }.ToString()); } std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>(); @@ -777,13 +782,15 @@ static UniValue submitheader(const JSONRPCRequest& request) "\nThrows when the header is invalid.\n", { {"hexdata", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded block header data"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "None" - "\nExamples:\n" + - HelpExampleCli("submitheader", "\"aabbcc\"") + - HelpExampleRpc("submitheader", "\"aabbcc\"")); + }, + RPCExamples{ + HelpExampleCli("submitheader", "\"aabbcc\"") + + HelpExampleRpc("submitheader", "\"aabbcc\"") + }, + }.ToString()); } CBlockHeader h; @@ -826,9 +833,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) " \"UNSET\"\n" " \"ECONOMICAL\"\n" " \"CONSERVATIVE\""}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" @@ -839,9 +845,11 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) "fee estimation is able to return based on how long it has been running.\n" "An error is returned if not enough transactions and blocks\n" "have been observed to make an estimate for any number of blocks.\n" - "\nExample:\n" - + HelpExampleCli("estimatesmartfee", "6") - ); + }, + RPCExamples{ + HelpExampleCli("estimatesmartfee", "6") + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR}); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); @@ -886,9 +894,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) {"threshold", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0.95", "The proportion of transactions in a given feerate range that must have been\n" " confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n" " lower buckets."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"short\" : { (json object, optional) estimate for short time horizon\n" " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" @@ -910,9 +917,11 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) "}\n" "\n" "Results are returned for any horizon which tracks blocks up to the confirmation target.\n" - "\nExample:\n" - + HelpExampleCli("estimaterawfee", "6 0.9") - ); + }, + RPCExamples{ + HelpExampleCli("estimaterawfee", "6 0.9") + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true); RPCTypeCheckArgument(request.params[0], UniValue::VNUM); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 18c867bff3..9d17a2fc4e 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -40,9 +40,8 @@ static UniValue validateaddress(const JSONRPCRequest& request) "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n", { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" @@ -52,10 +51,12 @@ static UniValue validateaddress(const JSONRPCRequest& request) " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + }, + RPCExamples{ + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - ); + }, + }.ToString()); CTxDestination dest = DecodeDestination(request.params[0].get_str()); bool isValid = IsValidDestination(dest); @@ -91,20 +92,20 @@ static UniValue createmultisig(const JSONRPCRequest& request) {"key", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded public key"}, }}, {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" "}\n" - - "\nExamples:\n" + }, + RPCExamples{ "\nCreate a multisig address from 2 public keys\n" + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") - ; + }, + }.ToString(); throw std::runtime_error(msg); } @@ -151,11 +152,11 @@ static UniValue verifymessage(const JSONRPCRequest& request) {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the signature."}, {"signature", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The signature provided by the signer in base 64 encoding (see signmessage)."}, {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message that was signed."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "true|false (boolean) If the signature is verified or not.\n" - "\nExamples:\n" + }, + RPCExamples{ "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" @@ -164,7 +165,8 @@ static UniValue verifymessage(const JSONRPCRequest& request) + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"") - ); + }, + }.ToString()); LOCK(cs_main); @@ -208,18 +210,19 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) { {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key to sign the message with."}, {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"signature\" (string) The signature of the message encoded in base 64\n" - "\nExamples:\n" + }, + RPCExamples{ "\nCreate the signature\n" + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"") - ); + }, + }.ToString()); std::string strPrivkey = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); @@ -249,8 +252,10 @@ static UniValue setmocktime(const JSONRPCRequest& request) { {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Unix seconds-since-epoch timestamp\n" " Pass 0 to go back to using the system time."}, - }} - .ToString() + }, + RPCResults{}, + RPCExamples{""}, + }.ToString() ); if (!Params().MineBlocksOnDemand()) @@ -314,9 +319,9 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request) {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"stats\"", "determines what kind of information is returned.\n" " - \"stats\" returns general statistics about memory usage in the daemon.\n" " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."}, - }} - .ToString() + - "\nResult (mode \"stats\"):\n" + }, + { + RPCResult{"mode \"stats\"", "{\n" " \"locked\": { (json object) Information about locked memory manager\n" " \"used\": xxxxx, (numeric) Number of bytes used\n" @@ -327,12 +332,16 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request) " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n" " }\n" "}\n" - "\nResult (mode \"mallocinfo\"):\n" + }, + RPCResult{"mode \"mallocinfo\"", "\"<malloc version=\"1\">...\"\n" - "\nExamples:\n" - + HelpExampleCli("getmemoryinfo", "") + }, + }, + RPCExamples{ + HelpExampleCli("getmemoryinfo", "") + HelpExampleRpc("getmemoryinfo", "") - ); + }, + }.ToString()); std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str(); if (mode == "stats") { @@ -392,17 +401,18 @@ UniValue logging(const JSONRPCRequest& request) { {"exclude_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"}, }}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object where keys are the logging categories, and values indicates its status\n" - " \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n" + " \"category\": true|false, (bool) if being debug logged or not. false:inactive, true:active\n" " ...\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") - + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"") - ); + }, + RPCExamples{ + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") + + HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]") + }, + }.ToString()); } uint32_t original_log_categories = g_logger->GetCategoryMask(); @@ -446,9 +456,11 @@ static UniValue echo(const JSONRPCRequest& request) "\nSimply echo back the input arguments. This command is for testing.\n" "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in " "bitcoin-cli and the GUI. There is no server-side difference.", - {}} - .ToString() + - ""); + {}, + RPCResults{}, + RPCExamples{""}, + }.ToString() + ); return request.params; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7994d3b125..b50038b085 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -30,14 +30,16 @@ static UniValue getconnectioncount(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getconnectioncount", - "\nReturns the number of connections to other nodes.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns the number of connections to other nodes.\n", + {}, + RPCResult{ "n (numeric) The connection count\n" - "\nExamples:\n" - + HelpExampleCli("getconnectioncount", "") + }, + RPCExamples{ + HelpExampleCli("getconnectioncount", "") + HelpExampleRpc("getconnectioncount", "") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -53,12 +55,13 @@ static UniValue ping(const JSONRPCRequest& request) "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n", - {}} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("ping", "") + {}, + RPCResults{}, + RPCExamples{ + HelpExampleCli("ping", "") + HelpExampleRpc("ping", "") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -75,9 +78,9 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getpeerinfo", - "\nReturns data about each connected network node as a json array of objects.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns data about each connected network node as a json array of objects.\n", + {}, + RPCResult{ "[\n" " {\n" " \"id\": n, (numeric) Peer index\n" @@ -124,10 +127,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) " }\n" " ,...\n" "]\n" - "\nExamples:\n" - + HelpExampleCli("getpeerinfo", "") + }, + RPCExamples{ + HelpExampleCli("getpeerinfo", "") + HelpExampleRpc("getpeerinfo", "") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -218,12 +223,13 @@ static UniValue addnode(const JSONRPCRequest& request) { {"node", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The node (see getpeerinfo for nodes)"}, {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -262,14 +268,15 @@ static UniValue disconnectnode(const JSONRPCRequest& request) { {"address", RPCArg::Type::STR, /* opt */ true, /* default_val */ "fallback to nodeid", "The IP address/port of the node"}, {"nodeid", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + HelpExampleCli("disconnectnode", "\"\" 1") + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") + HelpExampleRpc("disconnectnode", "\"\", 1") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -305,9 +312,8 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request) "(note that onetry addnodes are not listed here)\n", { {"node", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[\n" " {\n" " \"addednode\" : \"192.168.0.201\", (string) The node IP address or name (as provided to addnode)\n" @@ -321,10 +327,12 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request) " }\n" " ,...\n" "]\n" - "\nExamples:\n" - + HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") + }, + RPCExamples{ + HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -372,9 +380,8 @@ static UniValue getnettotals(const JSONRPCRequest& request) RPCHelpMan{"getnettotals", "\nReturns information about network traffic, including bytes in, bytes out,\n" "and current time.\n", - {}} - .ToString() + - "\nResult:\n" + {}, + RPCResult{ "{\n" " \"totalbytesrecv\": n, (numeric) Total bytes received\n" " \"totalbytessent\": n, (numeric) Total bytes sent\n" @@ -389,10 +396,12 @@ static UniValue getnettotals(const JSONRPCRequest& request) " \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n" " }\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getnettotals", "") + }, + RPCExamples{ + HelpExampleCli("getnettotals", "") + HelpExampleRpc("getnettotals", "") - ); + }, + }.ToString()); if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -438,9 +447,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getnetworkinfo", - "Returns an object containing various state info regarding P2P networking.\n", {}} - .ToString() + - "\nResult:\n" + "Returns an object containing various state info regarding P2P networking.\n", + {}, + RPCResult{ "{\n" " \"version\": xxxxx, (numeric) the server version\n" " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n" @@ -472,10 +481,12 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) " ]\n" " \"warnings\": \"...\" (string) any network and blockchain warnings\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getnetworkinfo", "") + }, + RPCExamples{ + HelpExampleCli("getnetworkinfo", "") + HelpExampleRpc("getnetworkinfo", "") - ); + }, + }.ToString()); LOCK(cs_main); UniValue obj(UniValue::VOBJ); @@ -525,13 +536,14 @@ static UniValue setban(const JSONRPCRequest& request) {"command", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"}, {"bantime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"}, {"absolute", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400") - ); + }, + }.ToString()); if (!g_banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } @@ -594,12 +606,14 @@ static UniValue listbanned(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"listbanned", - "\nList all banned IPs/Subnets.\n", {}} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("listbanned", "") + "\nList all banned IPs/Subnets.\n", + {}, + RPCResults{}, + RPCExamples{ + HelpExampleCli("listbanned", "") + HelpExampleRpc("listbanned", "") - ); + }, + }.ToString()); if(!g_banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); @@ -629,12 +643,14 @@ static UniValue clearbanned(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"clearbanned", - "\nClear all banned IPs.\n", {}} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("clearbanned", "") + "\nClear all banned IPs.\n", + {}, + RPCResults{}, + RPCExamples{ + HelpExampleCli("clearbanned", "") + HelpExampleRpc("clearbanned", "") - ); + }, + }.ToString()); if (!g_banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } @@ -652,8 +668,10 @@ static UniValue setnetworkactive(const JSONRPCRequest& request) "\nDisable/enable all p2p network activity.\n", { {"state", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "true to enable networking, false to disable"}, - }} - .ToString() + }, + RPCResults{}, + RPCExamples{""}, + }.ToString() ); } @@ -674,9 +692,8 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) "\nReturn known addresses which can potentially be used to find new nodes in the network\n", { {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[\n" " {\n" " \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n" @@ -686,10 +703,12 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) " }\n" " ,....\n" "]\n" - "\nExamples:\n" - + HelpExampleCli("getnodeaddresses", "8") + }, + RPCExamples{ + HelpExampleCli("getnodeaddresses", "8") + HelpExampleRpc("getnodeaddresses", "8") - ); + }, + }.ToString()); } if (!g_connman) { throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 91de72b70e..ac2e0ff4ee 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -67,12 +67,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( RPCHelpMan{"getrawtransaction", - "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n" - "enabled, it also works for blockchain transactions. If the block which contains the transaction\n" - "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n" - "provided, only that block will be searched and if the transaction is in the mempool or other\n" - "blocks, or if this node does not have the given block available, the transaction will not be found.\n" - "DEPRECATED: for now, it also works for transactions with unspent outputs.\n" + "\nBy default this function only works for mempool transactions. When called with a blockhash\n" + "argument, getrawtransaction will return the transaction if the specified block is available and\n" + "the transaction is found in that block. When called without a blockhash argument, getrawtransaction\n" + "will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n" + "is in a block in the blockchain.\n" "\nReturn the raw transaction data.\n" "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" @@ -82,12 +81,12 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"}, {"verbose", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If false, return a string, otherwise return a json object"}, {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "The block in which to look for the transaction"}, - }} - .ToString() + - "\nResult (if verbose is not set or set to false):\n" + }, + { + RPCResult{"if verbose is not set or set to false", "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" - - "\nResult (if verbose is set to true):\n" + }, + RPCResult{"if verbose is set to true", "{\n" " \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" @@ -133,14 +132,16 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" " \"time\" : ttt, (numeric) Same as \"blocktime\"\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("getrawtransaction", "\"mytxid\"") + }, + }, + RPCExamples{ + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"") - ); + }, + }.ToString()); bool in_active_chain = true; uint256 hash = ParseHashV(request.params[0], "parameter 1"); @@ -175,7 +176,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) CTransactionRef tx; uint256 hash_block; - if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) { + if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, blockindex)) { std::string errmsg; if (blockindex) { if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) { @@ -219,10 +220,12 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) }, }, {"blockhash", RPCArg::Type::STR_HEX, /* opt */ true, /* default_val */ "null", "If specified, looks for txid in the block with this hash"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" + }, + RPCExamples{""}, + }.ToString() ); std::set<uint256> setTxids; @@ -270,7 +273,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) if (pblockindex == nullptr) { CTransactionRef tx; - if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) + if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock) || hashBlock.IsNull()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); pblockindex = LookupBlockIndex(hashBlock); if (!pblockindex) { @@ -305,10 +308,12 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) "and throwing an RPC error if the block is not in our best chain\n", { {"proof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded proof generated by gettxoutproof"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n" + }, + RPCExamples{""}, + }.ToString() ); CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); @@ -494,17 +499,17 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"}, {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125-replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"transaction\" (string) hex string of the transaction\n" - - "\nExamples:\n" - + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"") + }, + RPCExamples{ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"") + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"") - ); + }, + }.ToString()); } RPCTypeCheck(request.params, { @@ -530,9 +535,8 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction hex string"}, {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction\n" " If iswitness is not present, heuristic tests will be used in decoding"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"txid\" : \"id\", (string) The transaction id\n" " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" @@ -572,11 +576,12 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) " ,...\n" " ],\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + }, + RPCExamples{ + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); @@ -603,9 +608,8 @@ static UniValue decodescript(const JSONRPCRequest& request) "\nDecode a hex-encoded script.\n", { {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "the hex-encoded script"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"asm\":\"asm\", (string) Script public key\n" " \"hex\":\"hex\", (string) hex-encoded public key\n" @@ -617,10 +621,12 @@ static UniValue decodescript(const JSONRPCRequest& request) " ],\n" " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("decodescript", "\"hexstring\"") + }, + RPCExamples{ + HelpExampleCli("decodescript", "\"hexstring\"") + HelpExampleRpc("decodescript", "\"hexstring\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR}); @@ -706,14 +712,14 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A transaction hash"}, }, }, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" - - "\nExamples:\n" - + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]") - ); + }, + RPCExamples{ + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]") + }, + }.ToString()); UniValue txs = request.params[0].get_array(); @@ -953,9 +959,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\"\n" }, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" @@ -970,11 +975,12 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) " ,...\n" " ]\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("signrawtransactionwithkey", "\"myhex\"") + }, + RPCExamples{ + HelpExampleCli("signrawtransactionwithkey", "\"myhex\"") + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); @@ -1015,11 +1021,11 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) { {"hexstring", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex string of the raw transaction"}, {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"hex\" (string) The transaction hash in hex\n" - "\nExamples:\n" + }, + RPCExamples{ "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" @@ -1028,7 +1034,8 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") - ); + }, + }.ToString()); std::promise<void> promise; @@ -1118,9 +1125,8 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) }, }, {"allowhighfees", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Allow high fees"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" " Length is exactly one for now.\n" " {\n" @@ -1129,7 +1135,8 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) " \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n" " }\n" "]\n" - "\nExamples:\n" + }, + RPCExamples{ "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" @@ -1138,7 +1145,8 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) + HelpExampleCli("testmempoolaccept", "[\"signedhex\"]") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]") - ); + }, + }.ToString()); } RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL}); @@ -1212,9 +1220,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n", { {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The PSBT base64 string"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n" " ... The layout is the same as the output of decoderawtransaction.\n" @@ -1301,10 +1308,11 @@ UniValue decodepsbt(const JSONRPCRequest& request) " ]\n" " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("decodepsbt", "\"psbt\"") - ); + }, + RPCExamples{ + HelpExampleCli("decodepsbt", "\"psbt\"") + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR}); @@ -1492,13 +1500,14 @@ UniValue combinepsbt(const JSONRPCRequest& request) {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"}, }, }, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ " \"psbt\" (string) The base64-encoded partially signed transaction\n" - "\nExamples:\n" - + HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]") - ); + }, + RPCExamples{ + HelpExampleCli("combinepsbt", "[\"mybase64_1\", \"mybase64_2\", \"mybase64_3\"]") + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VARR}, true); @@ -1546,19 +1555,19 @@ UniValue finalizepsbt(const JSONRPCRequest& request) {"psbt", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "A base64 string of a PSBT"}, {"extract", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "If true and the transaction is complete,\n" " extract and return the complete transaction in normal network serialization instead of the PSBT."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction if not extracted\n" " \"hex\" : \"value\", (string) The hex-encoded network transaction if extracted\n" " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" " ]\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("finalizepsbt", "\"psbt\"") - ); + }, + RPCExamples{ + HelpExampleCli("finalizepsbt", "\"psbt\"") + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true); @@ -1637,13 +1646,14 @@ UniValue createpsbt(const JSONRPCRequest& request) {"locktime", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"}, {"replaceable", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Marks this transaction as BIP125 replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" - "\nExamples:\n" - + HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") - ); + }, + RPCExamples{ + HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + }, + }.ToString()); RPCTypeCheck(request.params, { @@ -1688,16 +1698,17 @@ UniValue converttopsbt(const JSONRPCRequest& request) " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n" " will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n" " permitsigdata is true."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" - "\nExamples:\n" + }, + RPCExamples{ "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + "\nConvert the transaction to a PSBT\n" + HelpExampleCli("converttopsbt", "\"rawtransaction\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index edaf64f3e1..2ed74547b9 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -51,7 +51,7 @@ struct RPCCommandExecution explicit RPCCommandExecution(const std::string& method) { LOCK(g_rpc_server_info.mutex); - it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.cend(), {method, GetTimeMicros()}); + it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, GetTimeMicros()}); } ~RPCCommandExecution() { @@ -231,10 +231,12 @@ UniValue help(const JSONRPCRequest& jsonRequest) "\nList all commands, or get help for a specified command.\n", { {"command", RPCArg::Type::STR, /* opt */ true, /* default_val */ "all commands", "The command to get help on"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"text\" (string) The help text\n" + }, + RPCExamples{""}, + }.ToString() ); std::string strCommand; @@ -254,8 +256,11 @@ UniValue stop(const JSONRPCRequest& jsonRequest) if (jsonRequest.fHelp || jsonRequest.params.size() > 1) throw std::runtime_error( RPCHelpMan{"stop", - "\nStop Bitcoin server.", {}} - .ToString()); + "\nStop Bitcoin server.", + {}, + RPCResults{}, + RPCExamples{""}, + }.ToString()); // Event loop will exit after current HTTP requests have been handled, so // this reply will get back to the client. StartShutdown(); @@ -270,14 +275,16 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest) if (jsonRequest.fHelp || jsonRequest.params.size() > 0) throw std::runtime_error( RPCHelpMan{"uptime", - "\nReturns the total uptime of the server.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns the total uptime of the server.\n", + {}, + RPCResult{ "ttt (numeric) The number of seconds that the server has been running\n" - "\nExamples:\n" - + HelpExampleCli("uptime", "") + }, + RPCExamples{ + HelpExampleCli("uptime", "") + HelpExampleRpc("uptime", "") - ); + }, + }.ToString()); return GetTime() - GetStartupTime(); } @@ -287,8 +294,11 @@ static UniValue getrpcinfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 0) { throw std::runtime_error( RPCHelpMan{"getrpcinfo", - "\nReturns details of the RPC server.\n", {}} - .ToString() + "\nReturns details of the RPC server.\n", + {}, + RPCResults{}, + RPCExamples{""}, + }.ToString() ); } diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index b91baee4ac..4275cc09a8 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -242,8 +242,12 @@ struct Sections { } }; -RPCHelpMan::RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args) - : m_name{name}, m_description{description}, m_args{args} +RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples) + : m_name{std::move(name)}, + m_description{std::move(description)}, + m_args{std::move(args)}, + m_results{std::move(results)}, + m_examples{std::move(examples)} { std::set<std::string> named_args; for (const auto& arg : m_args) { @@ -252,6 +256,25 @@ RPCHelpMan::RPCHelpMan(const std::string& name, const std::string& description, } } +std::string RPCResults::ToDescriptionString() const +{ + std::string result; + for (const auto& r : m_results) { + if (r.m_cond.empty()) { + result += "\nResult:\n"; + } else { + result += "\nResult (" + r.m_cond + "):\n"; + } + result += r.m_result; + } + return result; +} + +std::string RPCExamples::ToDescriptionString() const +{ + return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples; +} + std::string RPCHelpMan::ToString() const { std::string ret; @@ -292,6 +315,12 @@ std::string RPCHelpMan::ToString() const } ret += sections.ToString(); + // Result + ret += m_results.ToDescriptionString(); + + // Examples + ret += m_examples.ToDescriptionString(); + return ret; } diff --git a/src/rpc/util.h b/src/rpc/util.h index 9e97b3ae0b..4a9d4be787 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -106,10 +106,62 @@ struct RPCArg { std::string ToDescriptionString(bool implicitly_required = false) const; }; +struct RPCResult { + const std::string m_cond; + const std::string m_result; + + explicit RPCResult(std::string result) + : m_cond{}, m_result{std::move(result)} + { + assert(!m_result.empty()); + } + + RPCResult(std::string cond, std::string result) + : m_cond{std::move(cond)}, m_result{std::move(result)} + { + assert(!m_cond.empty()); + assert(!m_result.empty()); + } +}; + +struct RPCResults { + const std::vector<RPCResult> m_results; + + RPCResults() + : m_results{} + { + } + + RPCResults(RPCResult result) + : m_results{{result}} + { + } + + RPCResults(std::initializer_list<RPCResult> results) + : m_results{results} + { + } + + /** + * Return the description string. + */ + std::string ToDescriptionString() const; +}; + +struct RPCExamples { + const std::string m_examples; + RPCExamples( + std::string examples) + : m_examples(std::move(examples)) + { + } + std::string ToDescriptionString() const; +}; + class RPCHelpMan { public: - RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args); + RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples); std::string ToString() const; @@ -117,6 +169,8 @@ private: const std::string m_name; const std::string m_description; const std::vector<RPCArg> m_args; + const RPCResults m_results; + const RPCExamples m_examples; }; #endif // BITCOIN_RPC_UTIL_H diff --git a/src/streams.h b/src/streams.h index 0809c96be1..4e600f1826 100644 --- a/src/streams.h +++ b/src/streams.h @@ -139,7 +139,7 @@ private: public: - /* + /** * @param[in] type Serialization Type * @param[in] version Serialization Version (including any flags) * @param[in] data Referenced byte vector to overwrite/append @@ -153,7 +153,7 @@ public: } } - /* + /** * (other params same as above) * @param[in] args A list of items to deserialize starting at pos. */ @@ -715,15 +715,15 @@ private: const int nType; const int nVersion; - FILE *src; // source file - uint64_t nSrcPos; // how many bytes have been read from source - uint64_t nReadPos; // how many bytes have been read from this - uint64_t nReadLimit; // up to which position we're allowed to read - uint64_t nRewind; // how many bytes we guarantee to rewind - std::vector<char> vchBuf; // the buffer + FILE *src; //!< source file + uint64_t nSrcPos; //!< how many bytes have been read from source + uint64_t nReadPos; //!< how many bytes have been read from this + uint64_t nReadLimit; //!< up to which position we're allowed to read + uint64_t nRewind; //!< how many bytes we guarantee to rewind + std::vector<char> vchBuf; //!< the buffer protected: - // read data from the source to fill the buffer + //! read data from the source to fill the buffer bool Fill() { unsigned int pos = nSrcPos % vchBuf.size(); unsigned int readNow = vchBuf.size() - pos; @@ -768,12 +768,12 @@ public: } } - // check whether we're at the end of the source file + //! check whether we're at the end of the source file bool eof() const { return nReadPos == nSrcPos && feof(src); } - // read a number of bytes + //! read a number of bytes void read(char *pch, size_t nSize) { if (nSize + nReadPos > nReadLimit) throw std::ios_base::failure("Read attempted past buffer limit"); @@ -795,12 +795,12 @@ public: } } - // return the current reading position + //! return the current reading position uint64_t GetPos() const { return nReadPos; } - // rewind to a given reading position + //! rewind to a given reading position bool SetPos(uint64_t nPos) { nReadPos = nPos; if (nReadPos + nRewind < nSrcPos) { @@ -826,8 +826,8 @@ public: return true; } - // prevent reading beyond a certain position - // no argument removes the limit + //! prevent reading beyond a certain position + //! no argument removes the limit bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) { if (nPos < nReadPos) return false; @@ -842,7 +842,7 @@ public: return (*this); } - // search for a given byte in the stream, and remain positioned on it + //! search for a given byte in the stream, and remain positioned on it void FindByte(char ch) { while (true) { if (nReadPos == nSrcPos) diff --git a/src/sync.cpp b/src/sync.cpp index 30811f5f89..23ca866e53 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -73,7 +73,11 @@ struct LockData { LockOrders lockorders; InvLockOrders invlockorders; std::mutex dd_mutex; -} static lockdata; +}; +LockData& GetLockData() { + static LockData lockdata; + return lockdata; +} static thread_local LockStack g_lockstack; @@ -109,6 +113,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, static void push_lock(void* c, const CLockLocation& locklocation) { + LockData& lockdata = GetLockData(); std::lock_guard<std::mutex> lock(lockdata.dd_mutex); g_lockstack.push_back(std::make_pair(c, locklocation)); @@ -173,6 +178,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi void DeleteLock(void* cs) { + LockData& lockdata = GetLockData(); if (!lockdata.available) { // We're already shutting down. return; diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp new file mode 100644 index 0000000000..0709da5563 --- /dev/null +++ b/src/test/fuzz/fuzz.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2009-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/fuzz/fuzz.h> + +#include <unistd.h> + +#include <pubkey.h> +#include <util/memory.h> + + +static bool read_stdin(std::vector<uint8_t>& data) +{ + uint8_t buffer[1024]; + ssize_t length = 0; + while ((length = read(STDIN_FILENO, buffer, 1024)) > 0) { + data.insert(data.end(), buffer, buffer + length); + + if (data.size() > (1 << 20)) return false; + } + return length == 0; +} + +static void initialize() +{ + const static auto verify_handle = MakeUnique<ECCVerifyHandle>(); +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + test_one_input(std::vector<uint8_t>(data, data + size)); + return 0; +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) +{ + initialize(); + return 0; +} + +// Disabled under WIN32 due to clash with Cygwin's WinMain. +#ifndef WIN32 +// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides +// the main(...) function. +__attribute__((weak)) +#endif +int main(int argc, char **argv) +{ + initialize(); +#ifdef __AFL_INIT + // Enable AFL deferred forkserver mode. Requires compilation using + // afl-clang-fast++. See fuzzing.md for details. + __AFL_INIT(); +#endif + +#ifdef __AFL_LOOP + // Enable AFL persistent mode. Requires compilation using afl-clang-fast++. + // See fuzzing.md for details. + while (__AFL_LOOP(1000)) { + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + continue; + } + test_one_input(buffer); + } +#else + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + return 0; + } + test_one_input(buffer); +#endif + return 0; +} diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h new file mode 100644 index 0000000000..ad62a5faf0 --- /dev/null +++ b/src/test/fuzz/fuzz.h @@ -0,0 +1,17 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_FUZZ_FUZZ_H +#define BITCOIN_TEST_FUZZ_FUZZ_H + +#include <functional> +#include <stdint.h> +#include <vector> + + +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + +void test_one_input(std::vector<uint8_t> buffer); + +#endif // BITCOIN_TEST_FUZZ_FUZZ_H diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index 88c082ff66..859fba0bdc 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif - #include <addrman.h> #include <blockencodings.h> #include <chain.h> @@ -28,304 +24,144 @@ #include <memory> #include <vector> -const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; - -enum TEST_ID { - CBLOCK_DESERIALIZE=0, - CTRANSACTION_DESERIALIZE, - CBLOCKLOCATOR_DESERIALIZE, - CBLOCKMERKLEROOT, - CADDRMAN_DESERIALIZE, - CBLOCKHEADER_DESERIALIZE, - CBANENTRY_DESERIALIZE, - CTXUNDO_DESERIALIZE, - CBLOCKUNDO_DESERIALIZE, - CCOINS_DESERIALIZE, - CNETADDR_DESERIALIZE, - CSERVICE_DESERIALIZE, - CMESSAGEHEADER_DESERIALIZE, - CADDRESS_DESERIALIZE, - CINV_DESERIALIZE, - CBLOOMFILTER_DESERIALIZE, - CDISKBLOCKINDEX_DESERIALIZE, - CTXOUTCOMPRESSOR_DESERIALIZE, - BLOCKTRANSACTIONS_DESERIALIZE, - BLOCKTRANSACTIONSREQUEST_DESERIALIZE, - TEST_ID_END -}; - -static bool read_stdin(std::vector<uint8_t> &data) { - uint8_t buffer[1024]; - ssize_t length=0; - while((length = read(STDIN_FILENO, buffer, 1024)) > 0) { - data.insert(data.end(), buffer, buffer+length); - - if (data.size() > (1<<20)) return false; - } - return length==0; -} - -static int test_one_input(std::vector<uint8_t> buffer) { - if (buffer.size() < sizeof(uint32_t)) return 0; - - uint32_t test_id = 0xffffffff; - memcpy(&test_id, buffer.data(), sizeof(uint32_t)); - buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t)); - - if (test_id >= TEST_ID_END) return 0; +#include <test/fuzz/fuzz.h> +void test_one_input(std::vector<uint8_t> buffer) +{ CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { int nVersion; ds >> nVersion; ds.SetVersion(nVersion); } catch (const std::ios_base::failure& e) { - return 0; + return; } - switch(test_id) { - case CBLOCK_DESERIALIZE: - { +#if BLOCK_DESERIALIZE try { CBlock block; ds >> block; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CTRANSACTION_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif TRANSACTION_DESERIALIZE try { CTransaction tx(deserialize, ds); - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CBLOCKLOCATOR_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKLOCATOR_DESERIALIZE try { CBlockLocator bl; ds >> bl; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CBLOCKMERKLEROOT: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKMERKLEROOT try { CBlock block; ds >> block; bool mutated; BlockMerkleRoot(block, &mutated); - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CADDRMAN_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif ADDRMAN_DESERIALIZE try { CAddrMan am; ds >> am; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CBLOCKHEADER_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKHEADER_DESERIALIZE try { CBlockHeader bh; ds >> bh; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CBANENTRY_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BANENTRY_DESERIALIZE try { CBanEntry be; ds >> be; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CTXUNDO_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif TXUNDO_DESERIALIZE try { CTxUndo tu; ds >> tu; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CBLOCKUNDO_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKUNDO_DESERIALIZE try { CBlockUndo bu; ds >> bu; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CCOINS_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif COINS_DESERIALIZE try { Coin coin; ds >> coin; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CNETADDR_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif NETADDR_DESERIALIZE try { CNetAddr na; ds >> na; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CSERVICE_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif SERVICE_DESERIALIZE try { CService s; ds >> s; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CMESSAGEHEADER_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif MESSAGEHEADER_DESERIALIZE CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; try { CMessageHeader mh(pchMessageStart); ds >> mh; - if (!mh.IsValid(pchMessageStart)) {return 0;} - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CADDRESS_DESERIALIZE: - { + if (!mh.IsValid(pchMessageStart)) {return;} + } catch (const std::ios_base::failure& e) {return;} +#elif ADDRESS_DESERIALIZE try { CAddress a; ds >> a; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CINV_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif INV_DESERIALIZE try { CInv i; ds >> i; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CBLOOMFILTER_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOOMFILTER_DESERIALIZE try { CBloomFilter bf; ds >> bf; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CDISKBLOCKINDEX_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif DISKBLOCKINDEX_DESERIALIZE try { CDiskBlockIndex dbi; ds >> dbi; - } catch (const std::ios_base::failure& e) {return 0;} - break; - } - case CTXOUTCOMPRESSOR_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif TXOUTCOMPRESSOR_DESERIALIZE CTxOut to; CTxOutCompressor toc(to); try { ds >> toc; - } catch (const std::ios_base::failure& e) {return 0;} - - break; - } - case BLOCKTRANSACTIONS_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKTRANSACTIONS_DESERIALIZE try { BlockTransactions bt; ds >> bt; - } catch (const std::ios_base::failure& e) {return 0;} - - break; - } - case BLOCKTRANSACTIONSREQUEST_DESERIALIZE: - { + } catch (const std::ios_base::failure& e) {return;} +#elif BLOCKTRANSACTIONSREQUEST_DESERIALIZE try { BlockTransactionsRequest btr; ds >> btr; - } catch (const std::ios_base::failure& e) {return 0;} - - break; - } - default: - return 0; - } - return 0; -} - -static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; -void initialize() { - globalVerifyHandle = MakeUnique<ECCVerifyHandle>(); -} - -// This function is used by libFuzzer -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - test_one_input(std::vector<uint8_t>(data, data + size)); - return 0; -} - -// This function is used by libFuzzer -extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { - initialize(); - return 0; -} - -// Disabled under WIN32 due to clash with Cygwin's WinMain. -#ifndef WIN32 -// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides -// the main(...) function. -__attribute__((weak)) -#endif -int main(int argc, char **argv) -{ - initialize(); -#ifdef __AFL_INIT - // Enable AFL deferred forkserver mode. Requires compilation using - // afl-clang-fast++. See fuzzing.md for details. - __AFL_INIT(); -#endif - -#ifdef __AFL_LOOP - // Enable AFL persistent mode. Requires compilation using afl-clang-fast++. - // See fuzzing.md for details. - int ret = 0; - while (__AFL_LOOP(1000)) { - std::vector<uint8_t> buffer; - if (!read_stdin(buffer)) { - continue; - } - ret = test_one_input(buffer); - } - return ret; + } catch (const std::ios_base::failure& e) {return;} #else - std::vector<uint8_t> buffer; - if (!read_stdin(buffer)) { - return 0; - } - return test_one_input(buffer); +#error Need at least one fuzz target to compile #endif } diff --git a/src/validation.cpp b/src/validation.cpp index 6a26bf9baa..de9c0d96db 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1002,13 +1002,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa * Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock. * If blockIndex is provided, the transaction is fetched from the corresponding block. */ -bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex) +bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, const CBlockIndex* const block_index) { - CBlockIndex* pindexSlow = blockIndex; - LOCK(cs_main); - if (!blockIndex) { + if (!block_index) { CTransactionRef ptx = mempool.get(hash); if (ptx) { txOut = ptx; @@ -1018,20 +1016,13 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus if (g_txindex) { return g_txindex->FindTx(hash, hashBlock, txOut); } - - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - const Coin& coin = AccessByTxid(*pcoinsTip, hash); - if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight]; - } - } - - if (pindexSlow) { + } else { CBlock block; - if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) { + if (ReadBlockFromDisk(block, block_index, consensusParams)) { for (const auto& tx : block.vtx) { if (tx->GetHash() == hash) { txOut = tx; - hashBlock = pindexSlow->GetBlockHash(); + hashBlock = block_index->GetBlockHash(); return true; } } diff --git a/src/validation.h b/src/validation.h index c0ffc9b0e4..b16d8438d7 100644 --- a/src/validation.h +++ b/src/validation.h @@ -201,14 +201,14 @@ static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288; static const signed int DEFAULT_CHECKBLOCKS = 6; static const unsigned int DEFAULT_CHECKLEVEL = 3; -// Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat) +// Require that user allocate at least 550 MiB for block & undo files (blk???.dat and rev???.dat) // At 1MB per block, 288 blocks = 288MB. // Add 15% for Undo data = 331MB // Add 20% for Orphan block rate = 397MB // We want the low water mark after pruning to be at least 397 MB and since we prune in // full block file chunks, we need the high water mark which triggers the prune to be // one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB -// Setting the target to > than 550MB will make it likely we can respect the target. +// Setting the target to >= 550 MiB will make it likely we can respect the target. static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; /** @@ -269,7 +269,7 @@ void ThreadScriptCheck(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, bool fAllowSlow = false, CBlockIndex* blockIndex = nullptr); +bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, const CBlockIndex* const blockIndex = nullptr); /** * Find the best known block, and make it the tip of the block chain * diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 295dfcc63f..2e62a6979f 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -111,16 +111,16 @@ UniValue importprivkey(const JSONRPCRequest& request) throw std::runtime_error( RPCHelpMan{"importprivkey", "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n" - "Hint: use importmulti to import more than one private key.\n", + "Hint: use importmulti to import more than one private key.\n" + "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n", { {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key (see dumpprivkey)"}, {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "current label if address exists, otherwise \"\"", "An optional label"}, {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"}, - }} - .ToString() + - "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" - "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" - "\nExamples:\n" + }, + RPCResults{}, + RPCExamples{ "\nDump a private key\n" + HelpExampleCli("dumpprivkey", "\"myaddress\"") + "\nImport the private key with rescan\n" @@ -131,7 +131,8 @@ UniValue importprivkey(const JSONRPCRequest& request) + HelpExampleCli("importprivkey", "\"mykey\" \"\" false") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") - ); + }, + }.ToString()); WalletRescanReserver reserver(pwallet); @@ -209,16 +210,18 @@ UniValue abortrescan(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 0) throw std::runtime_error( RPCHelpMan{"abortrescan", - "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n", {}} - .ToString() + - "\nExamples:\n" + "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n", + {}, + RPCResults{}, + RPCExamples{ "\nImport a private key\n" + HelpExampleCli("importprivkey", "\"mykey\"") + "\nAbort the running wallet rescan\n" + HelpExampleCli("abortrescan", "") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("abortrescan", "") - ); + }, + }.ToString()); if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false; pwallet->AbortRescan(); @@ -272,27 +275,28 @@ UniValue importaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( RPCHelpMan{"importaddress", - "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n", + "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" + "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" + "If you have the full public key, you should call importpubkey instead of this.\n" + "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n" + "as change, and not show up in many RPCs.\n", { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The Bitcoin address (or hex-encoded script)"}, {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"}, {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"}, {"p2sh", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Add the P2SH version of the script as well"}, - }} - .ToString() + - "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" - "may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" - "If you have the full public key, you should call importpubkey instead of this.\n" - "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n" - "as change, and not show up in many RPCs.\n" - "\nExamples:\n" + }, + RPCResults{}, + RPCExamples{ "\nImport an address with rescan\n" + HelpExampleCli("importaddress", "\"myaddress\"") + "\nImport using a label without rescan\n" + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false") - ); + }, + }.ToString()); std::string strLabel; @@ -358,8 +362,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request) { {"rawtransaction", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A raw transaction in hex funding an already-existing address in wallet"}, {"txoutproof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex output from gettxoutproof that contains the transaction"}, - }} - .ToString() + }, + RPCResults{}, + RPCExamples{""}, + }.ToString() ); CMutableTransaction tx; @@ -379,8 +385,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request) if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) { auto locked_chain = pwallet->chain().lock(); - const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); - if (!pindex || !chainActive.Contains(pindex)) { + if (locked_chain->getBlockHeight(merkleBlock.header.GetHash()) == nullopt) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } @@ -423,13 +428,14 @@ UniValue removeprunedfunds(const JSONRPCRequest& request) "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n", { {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded id of the transaction you are deleting"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") + + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") - ); + }, + }.ToString()); auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -461,23 +467,24 @@ UniValue importpubkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( RPCHelpMan{"importpubkey", - "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n", + "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" + "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n", { {"pubkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The hex-encoded public key"}, {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"}, {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"}, - }} - .ToString() + - "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" - "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" - "\nExamples:\n" + }, + RPCResults{}, + RPCExamples{ "\nImport a public key with rescan\n" + HelpExampleCli("importpubkey", "\"mypubkey\"") + "\nImport using a label without rescan\n" + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false") - ); + }, + }.ToString()); std::string strLabel; @@ -538,16 +545,17 @@ UniValue importwallet(const JSONRPCRequest& request) "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n", { {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet file"}, - }} - .ToString() + - "\nExamples:\n" + }, + RPCResults{}, + RPCExamples{ "\nDump the wallet\n" + HelpExampleCli("dumpwallet", "\"test\"") + "\nImport the wallet\n" + HelpExampleCli("importwallet", "\"test\"") + "\nImport using the json rpc call\n" + HelpExampleRpc("importwallet", "\"test\"") - ); + }, + }.ToString()); if (fPruneMode) throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); @@ -570,7 +578,8 @@ UniValue importwallet(const JSONRPCRequest& request) if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); } - nTimeBegin = chainActive.Tip()->GetBlockTime(); + Optional<int> tip_height = locked_chain->getHeight(); + nTimeBegin = tip_height ? locked_chain->getBlockTime(*tip_height) : 0; int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); @@ -671,15 +680,16 @@ UniValue dumpprivkey(const JSONRPCRequest& request) "Then the importprivkey can be used with this output\n", { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for the private key"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"key\" (string) The private key\n" - "\nExamples:\n" - + HelpExampleCli("dumpprivkey", "\"myaddress\"") + }, + RPCExamples{ + HelpExampleCli("dumpprivkey", "\"myaddress\"") + HelpExampleCli("importprivkey", "\"mykey\"") + HelpExampleRpc("dumpprivkey", "\"myaddress\"") - ); + }, + }.ToString()); auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -720,16 +730,17 @@ UniValue dumpwallet(const JSONRPCRequest& request) "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n", { {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The filename with path (either absolute or relative to bitcoind)"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object)\n" " \"filename\" : { (string) The filename with full absolute path\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("dumpwallet", "\"test\"") + }, + RPCExamples{ + HelpExampleCli("dumpwallet", "\"test\"") + HelpExampleRpc("dumpwallet", "\"test\"") - ); + }, + }.ToString()); auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -773,8 +784,9 @@ UniValue dumpwallet(const JSONRPCRequest& request) // produce output file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD); file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime())); - file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); - file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime())); + const Optional<int> tip_height = locked_chain->getHeight(); + file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)"); + file << strprintf("# mined on %s\n", tip_height ? FormatISO8601DateTime(locked_chain->getBlockTime(*tip_height)) : "(missing block time)"); file << "\n"; // add the base58check encoded extended master if the wallet uses HD @@ -849,9 +861,9 @@ struct ImportData enum class ScriptContext { - TOP, //! Top-level scriptPubKey - P2SH, //! P2SH redeemScript - WITNESS_V0, //! P2WSH witnessScript + TOP, //!< Top-level scriptPubKey + P2SH, //!< P2SH redeemScript + WITNESS_V0, //!< P2WSH witnessScript }; // Analyse the provided scriptPubKey, determining which keys and which redeem scripts from the ImportData struct are needed to spend it, and mark them as used. @@ -1150,7 +1162,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) RPCHelpMan{"importmulti", "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n" "If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n" - "Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n", + "Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n" + "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n", { {"requests", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Data to be imported", { @@ -1191,17 +1205,18 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Stating if should rescan the blockchain after all imports"}, }, "\"options\""}, - }} - .ToString() + - "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n" - "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n" - "\nExamples:\n" + - HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, " - "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") + - HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") + - + }, + RPCResult{ "\nResponse is an array with the same size as the input that has the execution result :\n" - " [{\"success\": true}, {\"success\": true, \"warnings\": [\"Ignoring irrelevant private key\"]}, {\"success\": false, \"error\": {\"code\": -1, \"message\": \"Internal Server Error\"}}, ...]\n"); + " [{\"success\": true}, {\"success\": true, \"warnings\": [\"Ignoring irrelevant private key\"]}, {\"success\": false, \"error\": {\"code\": -1, \"message\": \"Internal Server Error\"}}, ...]\n" + }, + RPCExamples{ + HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, " + "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") + + HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") + }, + }.ToString() + ); RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ}); @@ -1234,15 +1249,16 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) EnsureWalletIsUnlocked(pwallet); // Verify all timestamps are present before importing any keys. - now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0; + const Optional<int> tip_height = locked_chain->getHeight(); + now = tip_height ? locked_chain->getBlockMedianTimePast(*tip_height) : 0; for (const UniValue& data : requests.getValues()) { GetImportTimestamp(data, now); } const int64_t minimumTimestamp = 1; - if (fRescan && chainActive.Tip()) { - nLowestTimestamp = chainActive.Tip()->GetBlockTime(); + if (fRescan && tip_height) { + nLowestTimestamp = locked_chain->getBlockTime(*tip_height); } else { fRescan = false; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5e036eb5df..213765209c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -102,7 +102,10 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); - entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); + int64_t block_time; + bool found_block = chain.findBlock(wtx.hashBlock, nullptr /* block */, &block_time); + assert(found_block); + entry.pushKV("blocktime", block_time); } else { entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); } @@ -157,14 +160,15 @@ static UniValue getnewaddress(const JSONRPCRequest& request) { {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."}, {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"address\" (string) The new bitcoin address\n" - "\nExamples:\n" - + HelpExampleCli("getnewaddress", "") + }, + RPCExamples{ + HelpExampleCli("getnewaddress", "") + HelpExampleRpc("getnewaddress", "") - ); + }, + }.ToString()); if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); @@ -217,14 +221,15 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request) "This is for use with raw transactions, NOT normal use.\n", { {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"address\" (string) The address\n" - "\nExamples:\n" - + HelpExampleCli("getrawchangeaddress", "") + }, + RPCExamples{ + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "") - ); + }, + }.ToString()); if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); @@ -273,12 +278,13 @@ static UniValue setlabel(const JSONRPCRequest& request) { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to be associated with a label."}, {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label to assign to the address."}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") - ); + }, + }.ToString()); LOCK(pwallet->cs_wallet); @@ -369,16 +375,17 @@ static UniValue sendtoaddress(const JSONRPCRequest& request) " \"UNSET\"\n" " \"ECONOMICAL\"\n" " \"CONSERVATIVE\""}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"txid\" (string) The transaction id.\n" - "\nExamples:\n" - + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + }, + RPCExamples{ + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -446,9 +453,8 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request) "\nLists groups of addresses which have had their common ownership\n" "made public by common use as inputs or as the resulting change\n" "in past transactions\n", - {}} - .ToString() + - "\nResult:\n" + {}, + RPCResult{ "[\n" " [\n" " [\n" @@ -460,10 +466,12 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request) " ]\n" " ,...\n" "]\n" - "\nExamples:\n" - + HelpExampleCli("listaddressgroupings", "") + }, + RPCExamples{ + HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", "") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -510,11 +518,11 @@ static UniValue signmessage(const JSONRPCRequest& request) { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the private key."}, {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"signature\" (string) The signature of the message encoded in base 64\n" - "\nExamples:\n" + }, + RPCExamples{ "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" @@ -523,7 +531,8 @@ static UniValue signmessage(const JSONRPCRequest& request) + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"") - ); + }, + }.ToString()); auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -575,11 +584,11 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for transactions."}, {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only include transactions confirmed at least this many times."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" - "\nExamples:\n" + }, + RPCExamples{ "\nThe amount from transactions with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + "\nThe amount including unconfirmed transactions, zero confirmations\n" @@ -588,7 +597,8 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -646,11 +656,11 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) { {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The selected label, may be the default label using \"\"."}, {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "Only include transactions confirmed at least this many times."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n" - "\nExamples:\n" + }, + RPCExamples{ "\nAmount received by the default label with at least 1 confirmation\n" + HelpExampleCli("getreceivedbylabel", "\"\"") + "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n" @@ -659,7 +669,8 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -718,18 +729,19 @@ static UniValue getbalance(const JSONRPCRequest& request) {"dummy", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "Remains for backward compatibility. Must be excluded or set to \"*\"."}, {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Only include transactions confirmed at least this many times."}, {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Also include balance in watch-only addresses (see 'importaddress')"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n" - "\nExamples:\n" + }, + RPCExamples{ "\nThe total amount in the wallet with 1 or more confirmations\n" + HelpExampleCli("getbalance", "") + "\nThe total amount in the wallet at least 6 blocks confirmed\n" + HelpExampleCli("getbalance", "\"*\" 6") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("getbalance", "\"*\", 6") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -768,8 +780,11 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request) if (request.fHelp || request.params.size() > 0) throw std::runtime_error( RPCHelpMan{"getunconfirmedbalance", - "Returns the server's total unconfirmed balance\n", {}} - .ToString()); + "Returns the server's total unconfirmed balance\n", + {}, + RPCResults{}, + RPCExamples{""}, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -819,12 +834,12 @@ static UniValue sendmany(const JSONRPCRequest& request) " \"UNSET\"\n" " \"ECONOMICAL\"\n" " \"CONSERVATIVE\""}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" " the number of addresses.\n" - "\nExamples:\n" + }, + RPCExamples{ "\nSend two amounts to two different addresses:\n" + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" @@ -833,7 +848,8 @@ static UniValue sendmany(const JSONRPCRequest& request) + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -964,19 +980,20 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) }, {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "A label to assign the addresses to."}, {"address_type", RPCArg::Type::STR, /* opt */ true, /* default_val */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" "}\n" - "\nExamples:\n" + }, + RPCExamples{ "\nAdd a multisig address from 2 addresses\n" + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") - ; + }, + }.ToString(); throw std::runtime_error(msg); } @@ -1193,9 +1210,8 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request) {"include_empty", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include addresses that haven't received any payments."}, {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses (see 'importaddress')."}, {"address_filter", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "If present, only return information on this address."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[\n" " {\n" " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" @@ -1210,13 +1226,14 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request) " }\n" " ,...\n" "]\n" - - "\nExamples:\n" - + HelpExampleCli("listreceivedbyaddress", "") + }, + RPCExamples{ + HelpExampleCli("listreceivedbyaddress", "") + HelpExampleCli("listreceivedbyaddress", "6 true") + HelpExampleRpc("listreceivedbyaddress", "6, true, true") + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1245,9 +1262,8 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request) {"minconf", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1", "The minimum number of confirmations before payments are included."}, {"include_empty", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include labels that haven't received any payments."}, {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses (see 'importaddress')."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[\n" " {\n" " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" @@ -1257,12 +1273,13 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request) " }\n" " ,...\n" "]\n" - - "\nExamples:\n" - + HelpExampleCli("listreceivedbylabel", "") + }, + RPCExamples{ + HelpExampleCli("listreceivedbylabel", "") + HelpExampleCli("listreceivedbylabel", "6 true") + HelpExampleRpc("listreceivedbylabel", "6, true, true") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1388,9 +1405,8 @@ UniValue listtransactions(const JSONRPCRequest& request) {"count", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "10", "The number of transactions to return"}, {"skip", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "The number of transactions to skip"}, {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Include transactions to watch-only addresses (see 'importaddress')"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[\n" " {\n" " \"address\":\"address\", (string) The bitcoin address of the transaction.\n" @@ -1422,15 +1438,16 @@ UniValue listtransactions(const JSONRPCRequest& request) " 'send' category of transactions.\n" " }\n" "]\n" - - "\nExamples:\n" + }, + RPCExamples{ "\nList the most recent 10 transactions in the systems\n" + HelpExampleCli("listtransactions", "") + "\nList transactions 100 to 120\n" + HelpExampleCli("listtransactions", "\"*\" 20 100") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("listtransactions", "\"*\", 20, 100") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1523,9 +1540,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request) {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Include transactions to watch-only addresses (see 'importaddress')"}, {"include_removed", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n" " (not guaranteed to work on pruned nodes)"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"transactions\": [\n" " \"address\":\"address\", (string) The bitcoin address of the transaction.\n" @@ -1560,11 +1576,13 @@ static UniValue listsinceblock(const JSONRPCRequest& request) " ],\n" " \"lastblock\": \"lastblockhash\" (string) The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("listsinceblock", "") + }, + RPCExamples{ + HelpExampleCli("listsinceblock", "") + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1573,24 +1591,19 @@ static UniValue listsinceblock(const JSONRPCRequest& request) auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); - const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. - const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain. + // The way the 'height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0. + Optional<int> height = MakeOptional(false, int()); // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain. + Optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain. int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; + uint256 blockId; if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { - uint256 blockId(ParseHashV(request.params[0], "blockhash")); - - paltindex = pindex = LookupBlockIndex(blockId); - if (!pindex) { + blockId = ParseHashV(request.params[0], "blockhash"); + height = locked_chain->findFork(blockId, &altheight); + if (!height) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - if (chainActive[pindex->nHeight] != pindex) { - // the block being asked for is a part of a deactivated chain; - // we don't want to depend on its perceived height in the block - // chain, we want to instead use the last common ancestor - pindex = chainActive.FindFork(pindex); - } } if (!request.params[1].isNull()) { @@ -1607,7 +1620,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request) bool include_removed = (request.params[3].isNull() || request.params[3].get_bool()); - int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; + const Optional<int> tip_height = locked_chain->getHeight(); + int depth = tip_height && height ? (1 + *tip_height - *height) : -1; UniValue transactions(UniValue::VARR); @@ -1622,9 +1636,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request) // when a reorg'd block is requested, we also list any relevant transactions // in the blocks of the chain that was detached UniValue removed(UniValue::VARR); - while (include_removed && paltindex && paltindex != pindex) { + while (include_removed && altheight && *altheight > *height) { CBlock block; - if (!ReadBlockFromDisk(block, paltindex, Params().GetConsensus())) { + if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } for (const CTransactionRef& tx : block.vtx) { @@ -1635,11 +1649,12 @@ static UniValue listsinceblock(const JSONRPCRequest& request) ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */); } } - paltindex = paltindex->pprev; + blockId = block.hashPrevBlock; + --*altheight; } - CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; - uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); + int last_height = tip_height ? *tip_height + 1 - target_confirms : -1; + uint256 lastblock = last_height >= 0 ? locked_chain->getBlockHash(last_height) : uint256(); UniValue ret(UniValue::VOBJ); ret.pushKV("transactions", transactions); @@ -1665,9 +1680,8 @@ static UniValue gettransaction(const JSONRPCRequest& request) { {"txid", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The transaction id"}, {"include_watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Whether to include watch-only addresses in balance calculation and details[]"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" @@ -1702,12 +1716,13 @@ static UniValue gettransaction(const JSONRPCRequest& request) " ],\n" " \"hex\" : \"data\" (string) Raw data for transaction\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + }, + RPCExamples{ + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1770,13 +1785,13 @@ static UniValue abandontransaction(const JSONRPCRequest& request) "It has no effect on transactions which are already abandoned.\n", { {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The transaction id"}, - }} - .ToString() + - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") - ); + }, + }.ToString()); } // Make sure the results are valid at least up to the most recent block @@ -1814,12 +1829,13 @@ static UniValue backupwallet(const JSONRPCRequest& request) "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n", { {"destination", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The destination directory or file"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("backupwallet", "\"backup.dat\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("backupwallet", "\"backup.dat\"") + HelpExampleRpc("backupwallet", "\"backup.dat\"") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1853,12 +1869,13 @@ static UniValue keypoolrefill(const JSONRPCRequest& request) HelpRequiringPassphrase(pwallet) + "\n", { {"newsize", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "100", "The new keypool size"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("keypoolrefill", "") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("keypoolrefill", "") + HelpExampleRpc("keypoolrefill", "") - ); + }, + }.ToString()); if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); @@ -1899,23 +1916,24 @@ static UniValue walletpassphrase(const JSONRPCRequest& request) throw std::runtime_error( RPCHelpMan{"walletpassphrase", "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" - "This is needed prior to performing transactions related to private keys such as sending bitcoins\n", + "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" + "\nNote:\n" + "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" + "time that overrides the old one.\n", { {"passphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet passphrase"}, {"timeout", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."}, - }} - .ToString() + - "\nNote:\n" - "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" - "time that overrides the old one.\n" - "\nExamples:\n" + }, + RPCResults{}, + RPCExamples{ "\nUnlock the wallet for 60 seconds\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + "\nLock the wallet again (before 60 seconds)\n" + HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") - ); + }, + }.ToString()); } auto locked_chain = pwallet->chain().lock(); @@ -1988,12 +2006,13 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request) { {"oldpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The current passphrase"}, {"newpassphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The new passphrase"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") - ); + }, + }.ToString()); } auto locked_chain = pwallet->chain().lock(); @@ -2040,9 +2059,9 @@ static UniValue walletlock(const JSONRPCRequest& request) "\nRemoves the wallet encryption key from memory, locking the wallet.\n" "After calling this method, you will need to call walletpassphrase again\n" "before being able to call any methods which require the wallet to be unlocked.\n", - {}} - .ToString() + - "\nExamples:\n" + {}, + RPCResults{}, + RPCExamples{ "\nSet the passphrase for 2 minutes to perform a transaction\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + "\nPerform a send (requires passphrase set)\n" @@ -2051,7 +2070,8 @@ static UniValue walletlock(const JSONRPCRequest& request) + HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("walletlock", "") - ); + }, + }.ToString()); } auto locked_chain = pwallet->chain().lock(); @@ -2087,9 +2107,9 @@ static UniValue encryptwallet(const JSONRPCRequest& request) "If the wallet is already encrypted, use the walletpassphrasechange call.\n", { {"passphrase", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."}, - }} - .ToString() + - "\nExamples:\n" + }, + RPCResults{}, + RPCExamples{ "\nEncrypt your wallet\n" + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n" @@ -2100,7 +2120,8 @@ static UniValue encryptwallet(const JSONRPCRequest& request) + HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") - ); + }, + }.ToString()); } auto locked_chain = pwallet->chain().lock(); @@ -2158,12 +2179,11 @@ static UniValue lockunspent(const JSONRPCRequest& request) }, }, }, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "true|false (boolean) Whether the command was successful or not\n" - - "\nExamples:\n" + }, + RPCExamples{ "\nList the unspent transactions\n" + HelpExampleCli("listunspent", "") + "\nLock an unspent transaction\n" @@ -2174,7 +2194,8 @@ static UniValue lockunspent(const JSONRPCRequest& request) + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -2270,9 +2291,8 @@ static UniValue listlockunspent(const JSONRPCRequest& request) RPCHelpMan{"listlockunspent", "\nReturns list of temporarily unspendable outputs.\n" "See the lockunspent call to lock and unlock transactions for spending.\n", - {}} - .ToString() + - "\nResult:\n" + {}, + RPCResult{ "[\n" " {\n" " \"txid\" : \"transactionid\", (string) The transaction id locked\n" @@ -2280,7 +2300,8 @@ static UniValue listlockunspent(const JSONRPCRequest& request) " }\n" " ,...\n" "]\n" - "\nExamples:\n" + }, + RPCExamples{ "\nList the unspent transactions\n" + HelpExampleCli("listunspent", "") + "\nLock an unspent transaction\n" @@ -2291,7 +2312,8 @@ static UniValue listlockunspent(const JSONRPCRequest& request) + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("listlockunspent", "") - ); + }, + }.ToString()); auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -2327,14 +2349,15 @@ static UniValue settxfee(const JSONRPCRequest& request) "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n", { {"amount", RPCArg::Type::AMOUNT, /* opt */ false, /* default_val */ "", "The transaction fee in " + CURRENCY_UNIT + "/kB"}, - }} - .ToString() + - "\nResult\n" + }, + RPCResult{ "true|false (boolean) Returns true if successful\n" - "\nExamples:\n" - + HelpExampleCli("settxfee", "0.00001") + }, + RPCExamples{ + HelpExampleCli("settxfee", "0.00001") + HelpExampleRpc("settxfee", "0.00001") - ); + }, + }.ToString()); } auto locked_chain = pwallet->chain().lock(); @@ -2366,9 +2389,9 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( RPCHelpMan{"getwalletinfo", - "Returns an object containing various wallet state info.\n", {}} - .ToString() + - "\nResult:\n" + "Returns an object containing various wallet state info.\n", + {}, + RPCResult{ "{\n" " \"walletname\": xxxxx, (string) the wallet name\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" @@ -2385,10 +2408,12 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n" " \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getwalletinfo", "") + }, + RPCExamples{ + HelpExampleCli("getwalletinfo", "") + HelpExampleRpc("getwalletinfo", "") - ); + }, + }.ToString()); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -2429,8 +2454,9 @@ static UniValue listwalletdir(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( RPCHelpMan{"listwalletdir", - "Returns a list of wallets in the wallet directory.\n", {}} - .ToString() + + "Returns a list of wallets in the wallet directory.\n", + {}, + RPCResult{ "{\n" " \"wallets\" : [ (json array of objects)\n" " {\n" @@ -2439,10 +2465,12 @@ static UniValue listwalletdir(const JSONRPCRequest& request) " ,...\n" " ]\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("listwalletdir", "") + }, + RPCExamples{ + HelpExampleCli("listwalletdir", "") + HelpExampleRpc("listwalletdir", "") - ); + }, + }.ToString()); } UniValue wallets(UniValue::VARR); @@ -2464,17 +2492,18 @@ static UniValue listwallets(const JSONRPCRequest& request) RPCHelpMan{"listwallets", "Returns a list of currently loaded wallets.\n" "For full information on the wallet, use \"getwalletinfo\"\n", - {}} - .ToString() + - "\nResult:\n" + {}, + RPCResult{ "[ (json array of strings)\n" " \"walletname\" (string) the wallet name\n" " ...\n" "]\n" - "\nExamples:\n" - + HelpExampleCli("listwallets", "") + }, + RPCExamples{ + HelpExampleCli("listwallets", "") + HelpExampleRpc("listwallets", "") - ); + }, + }.ToString()); UniValue obj(UniValue::VARR); @@ -2501,17 +2530,18 @@ static UniValue loadwallet(const JSONRPCRequest& request) "\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n", { {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet directory or .dat file."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"name\" : <wallet_name>, (string) The wallet name if loaded successfully.\n" " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("loadwallet", "\"test.dat\"") + }, + RPCExamples{ + HelpExampleCli("loadwallet", "\"test.dat\"") + HelpExampleRpc("loadwallet", "\"test.dat\"") - ); + }, + }.ToString()); WalletLocation location(request.params[0].get_str()); std::string error; @@ -2555,17 +2585,18 @@ static UniValue createwallet(const JSONRPCRequest& request) { {"wallet_name", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The name for the new wallet. If this is a path, the wallet will be created at the path location."}, {"disable_private_keys", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n" " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("createwallet", "\"testwallet\"") + }, + RPCExamples{ + HelpExampleCli("createwallet", "\"testwallet\"") + HelpExampleRpc("createwallet", "\"testwallet\"") - ); + }, + }.ToString()); } std::string error; std::string warning; @@ -2609,12 +2640,13 @@ static UniValue unloadwallet(const JSONRPCRequest& request) "Specifying the wallet name on a wallet endpoint is invalid.", { {"wallet_name", RPCArg::Type::STR, /* opt */ true, /* default_val */ "the wallet name from the RPC request", "The name of the wallet to unload."}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("unloadwallet", "wallet_name") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("unloadwallet", "wallet_name") + HelpExampleRpc("unloadwallet", "wallet_name") - ); + }, + }.ToString()); } std::string wallet_name; @@ -2656,12 +2688,15 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request) throw std::runtime_error( RPCHelpMan{"resendwallettransactions", "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" - "Intended only for testing; the wallet code periodically re-broadcasts\n", - {}} - .ToString() + - "automatically.\n" + "Intended only for testing; the wallet code periodically re-broadcasts\n" + "automatically.\n", + {}, + RPCResult{ "Returns an RPC error if -walletbroadcast is set to false.\n" "Returns array of transaction ids that were re-broadcast.\n" + }, + RPCExamples{""}, + }.ToString() ); if (!g_connman) @@ -2716,9 +2751,8 @@ static UniValue listunspent(const JSONRPCRequest& request) {"minimumSumAmount", RPCArg::Type::AMOUNT, /* opt */ true, /* default_val */ "unlimited", "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""}, }, "query_options"}, - }} - .ToString() + - "\nResult\n" + }, + RPCResult{ "[ (array of json object)\n" " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" @@ -2738,14 +2772,15 @@ static UniValue listunspent(const JSONRPCRequest& request) " }\n" " ,...\n" "]\n" - - "\nExamples\n" - + HelpExampleCli("listunspent", "") + }, + RPCExamples{ + HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'") + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ") - ); + }, + }.ToString()); int nMinDepth = 1; if (!request.params[0].isNull()) { @@ -3028,15 +3063,15 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request) "options"}, {"iswitness", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction \n" " If iswitness is not present, heuristic tests will be used in decoding"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" " \"changepos\": n (numeric) The position of the added change output, or -1\n" "}\n" - "\nExamples:\n" + }, + RPCExamples{ "\nCreate a transaction with no inputs\n" + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + "\nAdd sufficient unsigned inputs to meet the output value\n" @@ -3045,7 +3080,8 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + "\nSend the transaction\n" + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL}); @@ -3107,9 +3143,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) " \"ALL|ANYONECANPAY\"\n" " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\""}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" @@ -3124,11 +3159,12 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) " ,...\n" " ]\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + }, + RPCExamples{ + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true); @@ -3190,18 +3226,20 @@ static UniValue bumpfee(const JSONRPCRequest& request) " \"CONSERVATIVE\""}, }, "options"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"txid\": \"value\", (string) The id of the new transaction\n" " \"origfee\": n, (numeric) Fee of the replaced transaction\n" " \"fee\": n, (numeric) Fee of the new transaction\n" " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty)\n" "}\n" - "\nExamples:\n" + }, + RPCExamples{ "\nBump the fee, get the new transaction\'s txid\n" + - HelpExampleCli("bumpfee", "<txid>")); + HelpExampleCli("bumpfee", "<txid>") + }, + }.ToString()); } RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ}); @@ -3316,14 +3354,15 @@ UniValue generate(const JSONRPCRequest& request) { {"nblocks", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "How many blocks are generated immediately."}, {"maxtries", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "1000000", "How many iterations to try."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[ blockhashes ] (array) hashes of blocks generated\n" - "\nExamples:\n" + }, + RPCExamples{ "\nGenerate 11 blocks\n" + HelpExampleCli("generate", "11") - ); + }, + }.ToString()); } if (!IsDeprecatedRPCEnabled("generate")) { @@ -3369,18 +3408,19 @@ UniValue rescanblockchain(const JSONRPCRequest& request) "\nRescan the local blockchain for wallet related transactions.\n", { {"start_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "block height where the rescan should start"}, - {"stop_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "tip height", "the last block height that should be scanned"}, - }} - .ToString() + - "\nResult:\n" + {"stop_height", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."}, + }, + RPCResult{ "{\n" - " \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n" - " \"stop_height\" (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n" + " \"start_height\" (numeric) The block height where the rescan has started.\n" + " \"stop_height\" (numeric) The height of the last rescanned block.\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("rescanblockchain", "100000 120000") + }, + RPCExamples{ + HelpExampleCli("rescanblockchain", "100000 120000") + HelpExampleRpc("rescanblockchain", "100000, 120000") - ); + }, + }.ToString()); } WalletRescanReserver reserver(pwallet); @@ -3388,50 +3428,48 @@ UniValue rescanblockchain(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } - CBlockIndex *pindexStart = nullptr; - CBlockIndex *pindexStop = nullptr; - CBlockIndex *pChainTip = nullptr; + int start_height = 0; + uint256 start_block, stop_block; { auto locked_chain = pwallet->chain().lock(); - pindexStart = chainActive.Genesis(); - pChainTip = chainActive.Tip(); + Optional<int> tip_height = locked_chain->getHeight(); if (!request.params[0].isNull()) { - pindexStart = chainActive[request.params[0].get_int()]; - if (!pindexStart) { + start_height = request.params[0].get_int(); + if (start_height < 0 || !tip_height || start_height > *tip_height) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); } } + Optional<int> stop_height; if (!request.params[1].isNull()) { - pindexStop = chainActive[request.params[1].get_int()]; - if (!pindexStop) { + stop_height = request.params[1].get_int(); + if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); } - else if (pindexStop->nHeight < pindexStart->nHeight) { + else if (*stop_height < start_height) { throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height"); } } - } - // We can't rescan beyond non-pruned blocks, stop and throw an error - if (fPruneMode) { - auto locked_chain = pwallet->chain().lock(); - CBlockIndex *block = pindexStop ? pindexStop : pChainTip; - while (block && block->nHeight >= pindexStart->nHeight) { - if (!(block->nStatus & BLOCK_HAVE_DATA)) { - throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + // We can't rescan beyond non-pruned blocks, stop and throw an error + if (locked_chain->findPruned(start_height, stop_height)) { + throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + } + + if (tip_height) { + start_block = locked_chain->getBlockHash(start_height); + if (stop_height) { + stop_block = locked_chain->getBlockHash(*stop_height); } - block = block->pprev; } } - const CBlockIndex *failed_block, *stopBlock; CWallet::ScanResult result = - pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true); - switch (result) { + pwallet->ScanForWalletTransactions(start_block, stop_block, reserver, true /* fUpdate */); + switch (result.status) { case CWallet::ScanResult::SUCCESS: - break; // stopBlock set by ScanForWalletTransactions + break; case CWallet::ScanResult::FAILURE: throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files."); case CWallet::ScanResult::USER_ABORT: @@ -3439,8 +3477,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request) // no default case, so the compiler can warn about missing cases } UniValue response(UniValue::VOBJ); - response.pushKV("start_height", pindexStart->nHeight); - response.pushKV("stop_height", stopBlock->nHeight); + response.pushKV("start_height", start_height); + response.pushKV("stop_height", result.stop_height ? *result.stop_height : UniValue()); return response; } @@ -3579,9 +3617,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request) "to be in the wallet.\n", { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to get the information of."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n" @@ -3618,10 +3655,12 @@ UniValue getaddressinfo(const JSONRPCRequest& request) " },...\n" " ]\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + }, + RPCExamples{ + HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - ); + }, + }.ToString()); } LOCK(pwallet->cs_wallet); @@ -3705,18 +3744,19 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request) "\nReturns the list of addresses assigned the specified label.\n", { {"label", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The label."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{ (json object with addresses as keys)\n" " \"address\": { (json object with information about address)\n" " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" " },...\n" "}\n" - "\nExamples:\n" - + HelpExampleCli("getaddressesbylabel", "\"tabby\"") + }, + RPCExamples{ + HelpExampleCli("getaddressesbylabel", "\"tabby\"") + HelpExampleRpc("getaddressesbylabel", "\"tabby\"") - ); + }, + }.ToString()); LOCK(pwallet->cs_wallet); @@ -3752,14 +3792,14 @@ static UniValue listlabels(const JSONRPCRequest& request) "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n", { {"purpose", RPCArg::Type::STR, /* opt */ true, /* default_val */ "null", "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "[ (json array of string)\n" " \"label\", (string) Label name\n" " ...\n" "]\n" - "\nExamples:\n" + }, + RPCExamples{ "\nList all labels\n" + HelpExampleCli("listlabels", "") + "\nList labels that have receiving addresses\n" @@ -3768,7 +3808,8 @@ static UniValue listlabels(const JSONRPCRequest& request) + HelpExampleCli("listlabels", "send") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("listlabels", "receive") - ); + }, + }.ToString()); LOCK(pwallet->cs_wallet); @@ -3816,14 +3857,15 @@ UniValue sethdseed(const JSONRPCRequest& request) " keypool will be used until it has been depleted."}, {"seed", RPCArg::Type::STR, /* opt */ true, /* default_val */ "random seed", "The WIF private key to use as the new HD seed.\n" " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"}, - }} - .ToString() + - "\nExamples:\n" - + HelpExampleCli("sethdseed", "") + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("sethdseed", "") + HelpExampleCli("sethdseed", "false") + HelpExampleCli("sethdseed", "true \"wifkey\"") + HelpExampleRpc("sethdseed", "true, \"wifkey\"") - ); + }, + }.ToString()); } if (IsInitialBlockDownload()) { @@ -3960,18 +4002,18 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\""}, {"bip32derivs", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction\n" " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" " ]\n" "}\n" - - "\nExamples:\n" - + HelpExampleCli("walletprocesspsbt", "\"psbt\"") - ); + }, + RPCExamples{ + HelpExampleCli("walletprocesspsbt", "\"psbt\"") + }, + }.ToString()); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR}); @@ -4069,18 +4111,19 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) }, "options"}, {"bip32derivs", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"}, - }} - .ToString() + - "\nResult:\n" + }, + RPCResult{ "{\n" " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n" " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" " \"changepos\": n (numeric) The position of the added change output, or -1\n" "}\n" - "\nExamples:\n" + }, + RPCExamples{ "\nCreate a transaction with no inputs\n" + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") - ); + }, + }.ToString()); RPCTypeCheck(request.params, { UniValue::VARR, diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 0db22cf6fe..8c380f1257 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -39,7 +39,6 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) auto chain = interfaces::MakeChain(); // Cap last block file size, and mine new block in a new block file. - const CBlockIndex* const null_block = nullptr; CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); @@ -53,10 +52,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); - const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; - BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(nullptr, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); - BOOST_CHECK_EQUAL(failed_block, null_block); - BOOST_CHECK_EQUAL(stop_block, null_block); + CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); + BOOST_CHECK(result.failed_block.IsNull()); + BOOST_CHECK(result.stop_block.IsNull()); + BOOST_CHECK(!result.stop_height); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); } @@ -67,10 +67,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); - const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; - BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); - BOOST_CHECK_EQUAL(failed_block, null_block); - BOOST_CHECK_EQUAL(stop_block, newTip); + CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); + BOOST_CHECK(result.failed_block.IsNull()); + BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash()); + BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -85,10 +86,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); - const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; - BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE); - BOOST_CHECK_EQUAL(failed_block, oldTip); - BOOST_CHECK_EQUAL(stop_block, newTip); + CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE); + BOOST_CHECK_EQUAL(result.failed_block, oldTip->GetBlockHash()); + BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash()); + BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -102,10 +104,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); - const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; - BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE); - BOOST_CHECK_EQUAL(failed_block, newTip); - BOOST_CHECK_EQUAL(stop_block, null_block); + CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE); + BOOST_CHECK_EQUAL(result.failed_block, newTip->GetBlockHash()); + BOOST_CHECK(result.stop_block.IsNull()); + BOOST_CHECK(!result.stop_height); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0); } } @@ -276,7 +279,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 CWalletTx wtx(&wallet, MakeTransactionRef(tx)); if (block) { - wtx.SetMerkleBranch(block, 0); + wtx.SetMerkleBranch(block->GetBlockHash(), 0); } { LOCK(cs_main); @@ -340,11 +343,11 @@ public: AddKey(*wallet, coinbaseKey); WalletRescanReserver reserver(wallet.get()); reserver.reserve(); - const CBlockIndex* const null_block = nullptr; - const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1; - BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS); - BOOST_CHECK_EQUAL(stop_block, chainActive.Tip()); - BOOST_CHECK_EQUAL(failed_block, null_block); + CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); + BOOST_CHECK_EQUAL(result.stop_block, chainActive.Tip()->GetBlockHash()); + BOOST_CHECK_EQUAL(*result.stop_height, chainActive.Height()); + BOOST_CHECK(result.failed_block.IsNull()); } ~ListCoinsTestingSetup() @@ -372,7 +375,7 @@ public: LOCK(wallet->cs_wallet); auto it = wallet->mapWallet.find(tx->GetHash()); BOOST_CHECK(it != wallet->mapWallet.end()); - it->second.SetMerkleBranch(chainActive.Tip(), 1); + it->second.SetMerkleBranch(chainActive.Tip()->GetBlockHash(), 1); return it->second; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 74deb2dddc..757cb81795 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -935,19 +935,19 @@ void CWallet::LoadToWallet(const CWalletTx& wtxIn) } } -bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) +bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool fUpdate) { const CTransaction& tx = *ptx; { AssertLockHeld(cs_wallet); - if (pIndex != nullptr) { + if (!block_hash.IsNull()) { for (const CTxIn& txin : tx.vin) { std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout); while (range.first != range.second) { if (range.first->second != tx.GetHash()) { - WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); - MarkConflicted(pIndex->GetBlockHash(), range.first->second); + WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), block_hash.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(block_hash, range.first->second); } range.first++; } @@ -983,8 +983,8 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI CWalletTx wtx(this, ptx); // Get merkle branch if transaction was found in a block - if (pIndex != nullptr) - wtx.SetMerkleBranch(pIndex, posInBlock); + if (!block_hash.IsNull()) + wtx.SetMerkleBranch(block_hash, posInBlock); return AddToWallet(wtx, false); } @@ -1071,11 +1071,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) auto locked_chain = chain().lock(); LOCK(cs_wallet); - int conflictconfirms = 0; - CBlockIndex* pindex = LookupBlockIndex(hashBlock); - if (pindex && chainActive.Contains(pindex)) { - conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); - } + int conflictconfirms = -locked_chain->getBlockDepth(hashBlock); // If number of conflict confirms cannot be determined, this means // that the block is still unknown or not yet part of the main chain, // for example when loading the wallet during a reindex. Do nothing in that @@ -1121,8 +1117,8 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } } -void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock, bool update_tx) { - if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, update_tx)) +void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool update_tx) { + if (!AddToWalletIfInvolvingMe(ptx, block_hash, posInBlock, update_tx)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -1134,7 +1130,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) { auto locked_chain = chain().lock(); LOCK(cs_wallet); - SyncTransaction(ptx); + SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); auto it = mapWallet.find(ptx->GetHash()); if (it != mapWallet.end()) { @@ -1162,15 +1158,15 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const // the notification that the conflicted transaction was evicted. for (const CTransactionRef& ptx : vtxConflicted) { - SyncTransaction(ptx); + SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); TransactionRemovedFromMempool(ptx); } for (size_t i = 0; i < pblock->vtx.size(); i++) { - SyncTransaction(pblock->vtx[i], pindex, i); + SyncTransaction(pblock->vtx[i], pindex->GetBlockHash(), i); TransactionRemovedFromMempool(pblock->vtx[i]); } - m_last_block_processed = pindex; + m_last_block_processed = pindex->GetBlockHash(); } void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) { @@ -1178,7 +1174,7 @@ void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) { LOCK(cs_wallet); for (const CTransactionRef& ptx : pblock->vtx) { - SyncTransaction(ptx); + SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */); } } @@ -1195,9 +1191,8 @@ void CWallet::BlockUntilSyncedToCurrentChain() { // protected by cs_wallet instead of cs_main, but as long as we need // cs_main here anyway, it's easier to just call it cs_main-protected. auto locked_chain = chain().lock(); - const CBlockIndex* initialChainTip = chainActive.Tip(); - if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) { + if (!m_last_block_processed.IsNull() && locked_chain->isPotentialTip(m_last_block_processed)) { return; } } @@ -1403,6 +1398,7 @@ void CWallet::SetHDSeed(const CPubKey& seed) newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; newHdChain.seed_id = seed.GetID(); SetHDChain(newHdChain, false); + NotifyCanGetAddressesChanged(); } void CWallet::SetHDChain(const CHDChain& chain, bool memonly) @@ -1591,132 +1587,144 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r // Find starting block. May be null if nCreateTime is greater than the // highest blockchain timestamp, in which case there is nothing that needs // to be scanned. - CBlockIndex* startBlock = nullptr; + uint256 start_block; { auto locked_chain = chain().lock(); - startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); - WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); + const Optional<int> start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block); + const Optional<int> tip_height = locked_chain->getHeight(); + WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0); } - if (startBlock) { - const CBlockIndex *failedBlock, *stop_block; + if (!start_block.IsNull()) { // TODO: this should take into account failure by ScanResult::USER_ABORT - if (ScanResult::FAILURE == ScanForWalletTransactions(startBlock, nullptr, reserver, failedBlock, stop_block, update)) { - return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; + ScanResult result = ScanForWalletTransactions(start_block, {} /* stop_block */, reserver, update); + if (result.status == ScanResult::FAILURE) { + int64_t time_max; + if (!chain().findBlock(result.failed_block, nullptr /* block */, nullptr /* time */, &time_max)) { + throw std::logic_error("ScanForWalletTransactions returned invalid block hash"); + } + return time_max + TIMESTAMP_WINDOW + 1; } } return startTime; } /** - * Scan the block chain (starting in pindexStart) for transactions + * Scan the block chain (starting in start_block) for transactions * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. * - * @param[in] pindexStop if not a nullptr, the scan will stop at this block-index - * @param[out] failed_block if FAILURE is returned, the most recent block - * that could not be scanned, otherwise nullptr - * @param[out] stop_block the most recent block that could be scanned, - * otherwise nullptr if no block could be scanned + * @param[in] start_block if not null, the scan will start at this block instead + * of the genesis block + * @param[in] stop_block if not null, the scan will stop at this block instead + * of the chain tip * * @return ScanResult indicating success or failure of the scan. SUCCESS if * scan was successful. FAILURE if a complete rescan was not possible (due to * pruning or corruption). USER_ABORT if the rescan was aborted before it * could complete. * - * @pre Caller needs to make sure pindexStop (and the optional pindexStart) are on + * @pre Caller needs to make sure start_block (and the optional stop_block) are on * the main chain after to the addition of any new keys you want to detect * transactions for. */ -CWallet::ScanResult CWallet::ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate) +CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, const uint256& stop_block, const WalletRescanReserver& reserver, bool fUpdate) { int64_t nNow = GetTime(); - const CChainParams& chainParams = Params(); assert(reserver.isReserved()); - if (pindexStop) { - assert(pindexStop->nHeight >= pindexStart->nHeight); - } - const CBlockIndex* pindex = pindexStart; - failed_block = nullptr; - stop_block = nullptr; + uint256 block_hash = start_block; + ScanResult result; - if (pindex) WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight); + WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString()); { fAbortRescan = false; ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup - CBlockIndex* tip = nullptr; + uint256 tip_hash; + // The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0. + Optional<int> block_height = MakeOptional(false, int()); double progress_begin; double progress_end; { auto locked_chain = chain().lock(); - progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex); - if (pindexStop == nullptr) { - tip = chainActive.Tip(); - progress_end = GuessVerificationProgress(chainParams.TxData(), tip); - } else { - progress_end = GuessVerificationProgress(chainParams.TxData(), pindexStop); + if (Optional<int> tip_height = locked_chain->getHeight()) { + tip_hash = locked_chain->getBlockHash(*tip_height); } + block_height = locked_chain->getBlockHeight(block_hash); + progress_begin = chain().guessVerificationProgress(block_hash); + progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block); } double progress_current = progress_begin; - while (pindex && !fAbortRescan && !ShutdownRequested()) { - if (pindex->nHeight % 100 == 0 && progress_end - progress_begin > 0.0) { + while (block_height && !fAbortRescan && !ShutdownRequested()) { + if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) { ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100)))); } if (GetTime() >= nNow + 60) { nNow = GetTime(); - WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, progress_current); + WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current); } CBlock block; - if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { + if (chain().findBlock(block_hash, &block) && !block.IsNull()) { auto locked_chain = chain().lock(); LOCK(cs_wallet); - if (pindex && !chainActive.Contains(pindex)) { + if (!locked_chain->getBlockHeight(block_hash)) { // Abort scan if current block is no longer active, to prevent // marking transactions as coming from the wrong block. - failed_block = pindex; + // TODO: This should return success instead of failure, see + // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518 + result.failed_block = block_hash; + result.status = ScanResult::FAILURE; break; } for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { - SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate); + SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate); } // scan succeeded, record block as most recent successfully scanned - stop_block = pindex; + result.stop_block = block_hash; + result.stop_height = *block_height; } else { // could not scan block, keep scanning but record this block as the most recent failure - failed_block = pindex; + result.failed_block = block_hash; + result.status = ScanResult::FAILURE; } - if (pindex == pindexStop) { + if (block_hash == stop_block) { break; } { auto locked_chain = chain().lock(); - pindex = chainActive.Next(pindex); - progress_current = GuessVerificationProgress(chainParams.TxData(), pindex); - if (pindexStop == nullptr && tip != chainActive.Tip()) { - tip = chainActive.Tip(); + Optional<int> tip_height = locked_chain->getHeight(); + if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) { + // break successfully when rescan has reached the tip, or + // previous block is no longer on the chain due to a reorg + break; + } + + // increment block and verification progress + block_hash = locked_chain->getBlockHash(++*block_height); + progress_current = chain().guessVerificationProgress(block_hash); + + // handle updated tip hash + const uint256 prev_tip_hash = tip_hash; + tip_hash = locked_chain->getBlockHash(*tip_height); + if (stop_block.IsNull() && prev_tip_hash != tip_hash) { // in case the tip has changed, update progress max - progress_end = GuessVerificationProgress(chainParams.TxData(), tip); + progress_end = chain().guessVerificationProgress(tip_hash); } } } ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI - if (pindex && fAbortRescan) { - WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, progress_current); - return ScanResult::USER_ABORT; - } else if (pindex && ShutdownRequested()) { - WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, progress_current); - return ScanResult::USER_ABORT; + if (block_height && fAbortRescan) { + WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height.value_or(0), progress_current); + result.status = ScanResult::USER_ABORT; + } else if (block_height && ShutdownRequested()) { + WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height.value_or(0), progress_current); + result.status = ScanResult::USER_ABORT; } } - if (failed_block) { - return ScanResult::FAILURE; - } else { - return ScanResult::SUCCESS; - } + return result; } void CWallet::ReacceptWalletTransactions() @@ -2573,6 +2581,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain) */ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_chain) { + uint32_t const height = locked_chain.getHeight().value_or(-1); uint32_t locktime; // Discourage fee sniping. // @@ -2595,7 +2604,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_cha // now we ensure code won't be written that makes assumptions about // nLockTime that preclude a fix later. if (IsCurrentForAntiFeeSniping(locked_chain)) { - locktime = chainActive.Height(); + locktime = height; // Secondly occasionally randomly pick a nLockTime even further back, so // that transactions that are delayed after signing for whatever reason, @@ -2609,7 +2618,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_cha // unique "nLockTime fingerprint", set nLockTime to a constant. locktime = 0; } - assert(locktime <= (unsigned int)chainActive.Height()); + assert(locktime <= height); assert(locktime < LOCKTIME_THRESHOLD); return locktime; } @@ -3327,6 +3336,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size()); } } + NotifyCanGetAddressesChanged(); return true; } @@ -3371,6 +3381,7 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); WalletLogPrintf("keypool reserve %d\n", nIndex); } + NotifyCanGetAddressesChanged(); return true; } @@ -3395,6 +3406,7 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) setExternalKeyPool.insert(nIndex); } m_pool_key_to_index[pubkey.GetID()] = nIndex; + NotifyCanGetAddressesChanged(); } WalletLogPrintf("keypool return %d\n", nIndex); } @@ -3718,11 +3730,12 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C } // map in which we'll infer heights of other keys - CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin - std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; + const Optional<int> tip_height = locked_chain.getHeight(); + const int max_height = tip_height && *tip_height > 144 ? *tip_height - 144 : 0; // the tip can be reorganized; use a 144-block safety margin + std::map<CKeyID, int> mapKeyFirstBlock; for (const CKeyID &keyid : GetKeys()) { if (mapKeyBirth.count(keyid) == 0) - mapKeyFirstBlock[keyid] = pindexMax; + mapKeyFirstBlock[keyid] = max_height; } // if there are no such keys, we're done @@ -3733,17 +3746,15 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C for (const auto& entry : mapWallet) { // iterate over all wallet transactions... const CWalletTx &wtx = entry.second; - CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock); - if (pindex && chainActive.Contains(pindex)) { + if (Optional<int> height = locked_chain.getBlockHeight(wtx.hashBlock)) { // ... which are already in a block - int nHeight = pindex->nHeight; for (const CTxOut &txout : wtx.tx->vout) { // iterate over all their outputs for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *this)) { // ... and all their affected keys - std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid); - if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) - rit->second = pindex; + std::map<CKeyID, int>::iterator rit = mapKeyFirstBlock.find(keyid); + if (rit != mapKeyFirstBlock.end() && *height < rit->second) + rit->second = *height; } } } @@ -3751,7 +3762,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C // Extract block timestamps for those keys for (const auto& entry : mapKeyFirstBlock) - mapKeyBirth[entry.first] = entry.second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off + mapKeyBirth[entry.first] = locked_chain.getBlockTime(entry.second) - TIMESTAMP_WINDOW; // block times can be 2h off } /** @@ -3779,7 +3790,8 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const { unsigned int nTimeSmart = wtx.nTimeReceived; if (!wtx.hashUnset()) { - if (const CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock)) { + int64_t blocktime; + if (chain().findBlock(wtx.hashBlock, nullptr /* block */, &blocktime)) { int64_t latestNow = wtx.nTimeReceived; int64_t latestEntry = 0; @@ -3805,7 +3817,6 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const } } - int64_t blocktime = pindex->GetBlockTime(); nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else { WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.hashBlock.ToString()); @@ -4067,7 +4078,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, } auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup - walletInstance->ChainStateFlushed(chainActive.GetLocator()); + walletInstance->ChainStateFlushed(locked_chain->getLocator()); } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { // Make it impossible to disable private keys after creation InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile)); @@ -4154,58 +4165,67 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, // Try to top up keypool. No-op if the wallet is locked. walletInstance->TopUpKeyPool(); - LockAnnotation lock(::cs_main); // Temporary, for FindForkInGlobalIndex below. Removed in upcoming commit. auto locked_chain = chain.lock(); LOCK(walletInstance->cs_wallet); - CBlockIndex *pindexRescan = chainActive.Genesis(); + int rescan_height = 0; if (!gArgs.GetBoolArg("-rescan", false)) { WalletBatch batch(*walletInstance->database); CBlockLocator locator; - if (batch.ReadBestBlock(locator)) - pindexRescan = FindForkInGlobalIndex(chainActive, locator); + if (batch.ReadBestBlock(locator)) { + if (const Optional<int> fork_height = locked_chain->findLocatorFork(locator)) { + rescan_height = *fork_height; + } + } } - walletInstance->m_last_block_processed = chainActive.Tip(); + const Optional<int> tip_height = locked_chain->getHeight(); + if (tip_height) { + walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height); + } else { + walletInstance->m_last_block_processed.SetNull(); + } - if (chainActive.Tip() && chainActive.Tip() != pindexRescan) + if (tip_height && *tip_height != rescan_height) { //We can't rescan beyond non-pruned blocks, stop and throw an error //this might happen if a user uses an old wallet within a pruned node // or if he ran -disablewallet for a longer time, then decided to re-enable if (fPruneMode) { - CBlockIndex *block = chainActive.Tip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block) - block = block->pprev; + int block_height = *tip_height; + while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) { + --block_height; + } - if (pindexRescan != block) { + if (rescan_height != block_height) { InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)")); return nullptr; } } uiInterface.InitMessage(_("Rescanning...")); - walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); + walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height); // No need to read and scan block if block was created before // our wallet birthday (as adjusted for block time variability) - while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { - pindexRescan = chainActive.Next(pindexRescan); + if (walletInstance->nTimeFirstKey) { + if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) { + rescan_height = *first_block; + } } nStart = GetTimeMillis(); { WalletRescanReserver reserver(walletInstance.get()); - const CBlockIndex *stop_block, *failed_block; - if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, failed_block, stop_block, true))) { + if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) { InitError(_("Failed to rescan the wallet during initialization")); return nullptr; } } walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart); - walletInstance->ChainStateFlushed(chainActive.GetLocator()); + walletInstance->ChainStateFlushed(locked_chain->getLocator()); walletInstance->database->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 @@ -4282,10 +4302,10 @@ CWalletKey::CWalletKey(int64_t nExpires) nTimeExpires = nExpires; } -void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) +void CMerkleTx::SetMerkleBranch(const uint256& block_hash, int posInBlock) { // Update the tx's hashBlock - hashBlock = pindex->GetBlockHash(); + hashBlock = block_hash; // set the position of the transaction in the block nIndex = posInBlock; @@ -4298,12 +4318,7 @@ int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const AssertLockHeld(cs_main); - // Find the block it claims to be in - CBlockIndex* pindex = LookupBlockIndex(hashBlock); - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); + return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1); } int CMerkleTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6872fbad2d..06c7900ce1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -35,8 +35,8 @@ #include <vector> //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. -// This function will perform salvage on the wallet if requested, as long as only one wallet is -// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). +//! This function will perform salvage on the wallet if requested, as long as only one wallet is +//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files); //! Load wallet databases. @@ -55,10 +55,10 @@ void StopWallets(); void UnloadWallets(); //! Explicitly unload and delete the wallet. -// Blocks the current thread after signaling the unload intent so that all -// wallet clients release the wallet. -// Note that, when blocking is not required, the wallet is implicitly unloaded -// by the shared pointer deleter. +//! Blocks the current thread after signaling the unload intent so that all +//! wallet clients release the wallet. +//! Note that, when blocking is not required, the wallet is implicitly unloaded +//! by the shared pointer deleter. void UnloadWallet(std::shared_ptr<CWallet>&& wallet); bool AddWallet(const std::shared_ptr<CWallet>& wallet); @@ -95,7 +95,6 @@ static const bool DEFAULT_DISABLE_WALLET = false; //! Pre-calculated constants for input size estimation in *virtual size* static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91; -class CBlockIndex; class CCoinControl; class COutput; class CReserveKey; @@ -287,7 +286,7 @@ public: READWRITE(nIndex); } - void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock); + void SetMerkleBranch(const uint256& block_hash, int posInBlock); /** * Return depth of transaction in blockchain: @@ -588,8 +587,8 @@ public: int64_t nTimeCreated; int64_t nTimeExpires; std::string strComment; - //! todo: add something to note what created it (user, getnewaddress, change) - //! maybe should have a map<string, string> property map + // todo: add something to note what created it (user, getnewaddress, change) + // maybe should have a map<string, string> property map explicit CWalletKey(int64_t nExpires=0); @@ -667,7 +666,7 @@ private: * Abandoned state should probably be more carefully tracked via different * posInBlock signals or by checking mempool presence when necessary. */ - bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const uint256& block_hash, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ void MarkConflicted(const uint256& hashBlock, const uint256& hashTx); @@ -678,8 +677,8 @@ private: void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions. - * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */ - void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */ + void SyncTransaction(const CTransactionRef& tx, const uint256& block_hash, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* the HD chain data model (external chain counters) */ CHDChain hdChain; @@ -723,10 +722,8 @@ private: * Note that this is *not* how far we've processed, we may need some rescan * to have seen all transactions in the chain, but is only used to track * live BlockConnected callbacks. - * - * Protected by cs_main (see BlockUntilSyncedToCurrentChain) */ - const CBlockIndex* m_last_block_processed = nullptr; + uint256 m_last_block_processed; public: /* @@ -909,12 +906,22 @@ public: void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override; int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); - enum class ScanResult { - SUCCESS, - FAILURE, - USER_ABORT + struct ScanResult { + enum { SUCCESS, FAILURE, USER_ABORT } status = SUCCESS; + + //! Hash and height of most recent block that was successfully scanned. + //! Unset if no blocks were scanned due to read errors or the chain + //! being empty. + uint256 stop_block; + Optional<int> stop_height; + + //! Height of the most recent block that could not be scanned due to + //! read errors or pruning. Will be set if status is FAILURE, unset if + //! status is SUCCESS, and may or may not be set if status is + //! USER_ABORT. + uint256 failed_block; }; - ScanResult ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate = false); + ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -1087,6 +1094,9 @@ public: /** Watch-only address added */ boost::signals2::signal<void (bool fHaveWatchOnly)> NotifyWatchonlyChanged; + /** Keypool has new keys */ + boost::signals2::signal<void ()> NotifyCanGetAddressesChanged; + /** Inquire whether this wallet broadcasts transactions. */ bool GetBroadcastTransactions() const { return fBroadcastTransactions; } /** Set whether this wallet broadcasts transactions. */ diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp index d3eab46e5f..a34968ef7d 100644 --- a/src/zmq/zmqrpc.cpp +++ b/src/zmq/zmqrpc.cpp @@ -18,9 +18,9 @@ UniValue getzmqnotifications(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( RPCHelpMan{"getzmqnotifications", - "\nReturns information about the active ZeroMQ notifications.\n", {}} - .ToString() + - "\nResult:\n" + "\nReturns information about the active ZeroMQ notifications.\n", + {}, + RPCResult{ "[\n" " { (json object)\n" " \"type\": \"pubhashtx\", (string) Type of notification\n" @@ -29,10 +29,12 @@ UniValue getzmqnotifications(const JSONRPCRequest& request) " },\n" " ...\n" "]\n" - "\nExamples:\n" - + HelpExampleCli("getzmqnotifications", "") + }, + RPCExamples{ + HelpExampleCli("getzmqnotifications", "") + HelpExampleRpc("getzmqnotifications", "") - ); + }, + }.ToString()); } UniValue result(UniValue::VARR); |