diff options
82 files changed, 2672 insertions, 1792 deletions
diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h index 3178f2a3d8..47c0a5f407 100644 --- a/build_msvc/bitcoin_config.h +++ b/build_msvc/bitcoin_config.h @@ -258,12 +258,6 @@ /* Define if the visibility attribute is supported. */ #define HAVE_VISIBILITY_ATTRIBUTE 1 -/* Define this symbol if boost sleep works */ -/* #undef HAVE_WORKING_BOOST_SLEEP */ - -/* Define this symbol if boost sleep_for works */ -#define HAVE_WORKING_BOOST_SLEEP_FOR 1 - /* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh index 1cb5c30901..933f4cea91 100755 --- a/ci/test/05_before_script.sh +++ b/ci/test/05_before_script.sh @@ -26,7 +26,7 @@ if [[ $HOST = *-mingw32 ]]; then fi if [ -z "$NO_DEPENDS" ]; then if [[ $DOCKER_NAME_TAG == centos* ]]; then - # CentOS has problems building the depends if the config shell is not explicitely set + # CentOS has problems building the depends if the config shell is not explicitly set # (i.e. for libevent a Makefile with an empty SHELL variable is generated, leading to # an error as the first command is executed) SHELL_OPTS="CONFIG_SHELL=/bin/bash" diff --git a/configure.ac b/configure.ac index 35e5dc1fe7..8433615bb2 100644 --- a/configure.ac +++ b/configure.ac @@ -1244,57 +1244,6 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" -dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however -dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if -dnl a working version is available, else fall back to sleep. sleep was removed -dnl after 1.56. -dnl If neither is available, abort. -TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $LIBS" -TEMP_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include <boost/thread/thread.hpp> - #include <boost/version.hpp> - ]],[[ - #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) - boost::this_thread::sleep_for(boost::chrono::milliseconds(0)); - #else - choke me - #endif - ]])], - [boost_sleep=yes; - AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], - [boost_sleep=no]) -LIBS="$TEMP_LIBS" -CPPFLAGS="$TEMP_CPPFLAGS" - -if test x$boost_sleep != xyes; then -TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $LIBS" -TEMP_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include <boost/version.hpp> - #include <boost/thread.hpp> - #include <boost/date_time/posix_time/posix_time_types.hpp> - ]],[[ - #if BOOST_VERSION <= 105600 - boost::this_thread::sleep(boost::posix_time::milliseconds(0)); - #else - choke me - #endif - ]])], - [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])], - [boost_sleep=no]) -LIBS="$TEMP_LIBS" -CPPFLAGS="$TEMP_CPPFLAGS" -fi - -if test x$boost_sleep != xyes; then - AC_MSG_ERROR(No working boost sleep implementation found.) -fi - fi if test x$use_pkgconfig = xyes; then diff --git a/doc/release-notes-15437.md b/doc/release-notes-15437.md deleted file mode 100644 index 6614207757..0000000000 --- a/doc/release-notes-15437.md +++ /dev/null @@ -1,53 +0,0 @@ -P2P and network changes ------------------------ - -#### Removal of reject network messages from Bitcoin Core (BIP61) - -The command line option to enable BIP61 (`-enablebip61`) has been removed. - -This feature has been disabled by default since Bitcoin Core version 0.18.0. -Nodes on the network can not generally be trusted to send valid ("reject") -messages, so this should only ever be used when connected to a trusted node. -Please use the recommended alternatives if you rely on this deprecated feature: - -* Testing or debugging of implementations of the Bitcoin P2P network protocol - should be done by inspecting the log messages that are produced by a recent - version of Bitcoin Core. Bitcoin Core logs debug messages - (`-debug=<category>`) to a stream (`-printtoconsole`) or to a file - (`-debuglogfile=<debug.log>`). - -* Testing the validity of a block can be achieved by specific RPCs: - - `submitblock` - - `getblocktemplate` with `'mode'` set to `'proposal'` for blocks with - potentially invalid POW - -* Testing the validity of a transaction can be achieved by specific RPCs: - - `sendrawtransaction` - - `testmempoolaccept` - -* Wallets should not use the absence of "reject" messages to indicate a - transaction has propagated the network, nor should wallets use "reject" - messages to set transaction fees. Wallets should rather use fee estimation - to determine transaction fees and set replace-by-fee if desired. Thus, they - could wait until the transaction has confirmed (taking into account the fee - target they set (compare the RPC `estimatesmartfee`)) or listen for the - transaction announcement by other network peers to check for propagation. - -The removal of BIP61 REJECT message support also has the following minor RPC -and logging implications: - -* `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT - code when a transaction is not accepted to the mempool. They still return the - verbal reject reason. - -* Log messages that previously reported the REJECT code when a transaction was - not accepted to the mempool now no longer report the REJECT code. The reason - for rejection is still reported. - -Updated RPCs ------------- - -- `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT - code when a transaction is not accepted to the mempool. See the Section - _Removal of reject network messages from Bitcoin Core (BIP61)_ for details on - the removal of BIP61 REJECT message support. diff --git a/doc/release-notes-15954.md b/doc/release-notes-15954.md deleted file mode 100644 index f4d2c5688c..0000000000 --- a/doc/release-notes-15954.md +++ /dev/null @@ -1,4 +0,0 @@ -Configuration option changes ------------------------------ - -Importing blocks upon startup via the `bootstrap.dat` file no longer occurs by default. The file must now be specified with `-loadblock=<file>`. diff --git a/doc/release-notes-17056.md b/doc/release-notes-17056.md deleted file mode 100644 index 23d5a8c8cd..0000000000 --- a/doc/release-notes-17056.md +++ /dev/null @@ -1,4 +0,0 @@ -Low-level RPC Changes -=== - -- A new descriptor type `sortedmulti(...)` has been added to support multisig scripts where the public keys are sorted lexicographically in the resulting script. diff --git a/doc/release-notes-17264.md b/doc/release-notes-17264.md deleted file mode 100644 index f6e0979596..0000000000 --- a/doc/release-notes-17264.md +++ /dev/null @@ -1,4 +0,0 @@ -Updated RPCs ------------- - -- `walletprocesspsbt` and `walletcreatefundedpsbt` now include BIP 32 derivation paths by default for public keys if we know them. This can be disabled by setting `bip32derivs` to `false`. diff --git a/doc/release-notes-17410.md b/doc/release-notes-17410.md deleted file mode 100644 index 08ed353889..0000000000 --- a/doc/release-notes-17410.md +++ /dev/null @@ -1,5 +0,0 @@ -Command-line options --------------------- - -- The `-debug=db` logging category has been renamed to `-debug=walletdb`, to distinguish it from `coindb`. - `-debug=db` has been deprecated and will be removed in the next major release. diff --git a/doc/release-notes-17437.md b/doc/release-notes-17437.md deleted file mode 100644 index 3edfd00a38..0000000000 --- a/doc/release-notes-17437.md +++ /dev/null @@ -1,5 +0,0 @@ -Low-level RPC Changes -=== - -- The RPC gettransaction, listtransactions and listsinceblock responses now also -includes the height of the block that contains the wallet transaction, if any. diff --git a/doc/release-notes-17578.md b/doc/release-notes-17578.md deleted file mode 100644 index 664d17fd78..0000000000 --- a/doc/release-notes-17578.md +++ /dev/null @@ -1,13 +0,0 @@ -Deprecated or removed RPCs --------------------------- - -- RPC `getaddressinfo` changes: - - - the `label` field has been deprecated in favor of the `labels` field and - will be removed in 0.21. It can be re-enabled in the interim by launching - with `-deprecatedrpc=label`. - - - the `labels` behavior of returning an array of JSON objects containing name - and purpose key/value pairs has been deprecated in favor of an array of - label names and will be removed in 0.21. The previous behavior can be - re-enabled in the interim by launching with `-deprecatedrpc=labelspurpose`. diff --git a/doc/release-notes.md b/doc/release-notes.md index 99ca53c597..cb46408d94 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -62,6 +62,64 @@ distribution provides binaries for the RISC-V platform. Notable changes =============== +P2P and network changes +----------------------- + +#### Removal of reject network messages from Bitcoin Core (BIP61) + +The command line option to enable BIP61 (`-enablebip61`) has been removed. + +This feature has been disabled by default since Bitcoin Core version 0.18.0. +Nodes on the network can not generally be trusted to send valid ("reject") +messages, so this should only ever be used when connected to a trusted node. +Please use the recommended alternatives if you rely on this deprecated feature: + +* Testing or debugging of implementations of the Bitcoin P2P network protocol + should be done by inspecting the log messages that are produced by a recent + version of Bitcoin Core. Bitcoin Core logs debug messages + (`-debug=<category>`) to a stream (`-printtoconsole`) or to a file + (`-debuglogfile=<debug.log>`). + +* Testing the validity of a block can be achieved by specific RPCs: + - `submitblock` + - `getblocktemplate` with `'mode'` set to `'proposal'` for blocks with + potentially invalid POW + +* Testing the validity of a transaction can be achieved by specific RPCs: + - `sendrawtransaction` + - `testmempoolaccept` + +* Wallets should not use the absence of "reject" messages to indicate a + transaction has propagated the network, nor should wallets use "reject" + messages to set transaction fees. Wallets should rather use fee estimation + to determine transaction fees and set replace-by-fee if desired. Thus, they + could wait until the transaction has confirmed (taking into account the fee + target they set (compare the RPC `estimatesmartfee`)) or listen for the + transaction announcement by other network peers to check for propagation. + +The removal of BIP61 REJECT message support also has the following minor RPC +and logging implications: + +* `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT + code when a transaction is not accepted to the mempool. They still return the + verbal reject reason. + +* Log messages that previously reported the REJECT code when a transaction was + not accepted to the mempool now no longer report the REJECT code. The reason + for rejection is still reported. + +Updated RPCs +------------ + +- `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT + code when a transaction is not accepted to the mempool. See the Section + _Removal of reject network messages from Bitcoin Core (BIP61)_ for details on + the removal of BIP61 REJECT message support. + +- A new descriptor type `sortedmulti(...)` has been added to support multisig scripts where the public keys are sorted lexicographically in the resulting script. + +- `walletprocesspsbt` and `walletcreatefundedpsbt` now include BIP 32 derivation paths by default for public keys if we know them. This can be disabled by setting `bip32derivs` to `false`. + Build System ------------ @@ -84,11 +142,10 @@ It can be set with two command line arguments (`rpcwhitelist` and `rpcwhitelistd Updated settings ---------------- -Updated RPCs ------------- +Importing blocks upon startup via the `bootstrap.dat` file no longer occurs by default. The file must now be specified with `-loadblock=<file>`. -Note: some low-level RPC changes mainly useful for testing are described in the -Low-level Changes section below. +- The `-debug=db` logging category has been renamed to `-debug=walletdb`, to distinguish it from `coindb`. + `-debug=db` has been deprecated and will be removed in the next major release. GUI changes ----------- @@ -101,6 +158,20 @@ Wallet - The wallet now by default uses bech32 addresses when using RPC, and creates native segwit change outputs. - The way that output trust was computed has been fixed in #16766, which impacts confirmed/unconfirmed balance status and coin selection. +- The RPC gettransaction, listtransactions and listsinceblock responses now also +includes the height of the block that contains the wallet transaction, if any. + +- RPC `getaddressinfo` changes: + + - the `label` field has been deprecated in favor of the `labels` field and + will be removed in 0.21. It can be re-enabled in the interim by launching + with `-deprecatedrpc=label`. + + - the `labels` behavior of returning an array of JSON objects containing name + and purpose key/value pairs has been deprecated in favor of an array of + label names and will be removed in 0.21. The previous behavior can be + re-enabled in the interim by launching with `-deprecatedrpc=labelspurpose`. + Low-level changes ================= diff --git a/src/Makefile.am b/src/Makefile.am index 0a1c45cf8c..abd3bb881a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,7 +178,6 @@ BITCOIN_CORE_H = \ random.h \ randomenv.h \ reverse_iterator.h \ - reverselock.h \ rpc/blockchain.h \ rpc/client.h \ rpc/protocol.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index c474ae2442..66b9814759 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -31,10 +31,13 @@ FUZZ_TARGETS = \ test/fuzz/eval_script \ test/fuzz/fee_rate_deserialize \ test/fuzz/flat_file_pos_deserialize \ + test/fuzz/float \ test/fuzz/hex \ test/fuzz/integer \ test/fuzz/inv_deserialize \ + test/fuzz/key \ test/fuzz/key_origin_info_deserialize \ + test/fuzz/locale \ test/fuzz/merkle_block_deserialize \ test/fuzz/messageheader_deserialize \ test/fuzz/netaddr_deserialize \ @@ -405,6 +408,12 @@ test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_flat_file_pos_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_float_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_float_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_float_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_float_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_float_SOURCES = $(FUZZ_SUITE) test/fuzz/float.cpp + test_fuzz_hex_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_fuzz_hex_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_hex_LDADD = $(FUZZ_SUITE_LD_COMMON) @@ -423,12 +432,24 @@ test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_key_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_key_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_key_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_key_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_key_SOURCES = $(FUZZ_SUITE) test/fuzz/key.cpp + test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1 test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_key_origin_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_locale_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_locale_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_locale_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_locale_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_locale_SOURCES = $(FUZZ_SUITE) test/fuzz/locale.cpp + test_fuzz_merkle_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMERKLE_BLOCK_DESERIALIZE=1 test_fuzz_merkle_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_merkle_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) diff --git a/src/addrman.cpp b/src/addrman.cpp index 121ae4bf7e..2f8a3a0bd5 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -6,8 +6,8 @@ #include <addrman.h> #include <hash.h> -#include <serialize.h> #include <logging.h> +#include <serialize.h> int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const { @@ -15,7 +15,7 @@ int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asma uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash(); int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT; uint32_t mapped_as = GetMappedAS(asmap); - LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i.\n", ToStringIP(), mapped_as, tried_bucket); + LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket); return tried_bucket; } @@ -26,7 +26,7 @@ int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std: uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash(); int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT; uint32_t mapped_as = GetMappedAS(asmap); - LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i.\n", ToStringIP(), mapped_as, new_bucket); + LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket); return new_bucket; } @@ -630,12 +630,12 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path) FILE *filestr = fsbridge::fopen(path, "rb"); CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { - LogPrintf("Failed to open asmap file from disk.\n"); + LogPrintf("Failed to open asmap file from disk\n"); return bits; } fseek(filestr, 0, SEEK_END); int length = ftell(filestr); - LogPrintf("Opened asmap file %s (%d bytes) from disk.\n", path, length); + LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length); fseek(filestr, 0, SEEK_SET); char cur_byte; for (int i = 0; i < length; ++i) { diff --git a/src/addrman.h b/src/addrman.h index 5901611bee..8e82020df0 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -6,23 +6,22 @@ #ifndef BITCOIN_ADDRMAN_H #define BITCOIN_ADDRMAN_H +#include <clientversion.h> #include <netaddress.h> #include <protocol.h> #include <random.h> #include <sync.h> #include <timedata.h> #include <util/system.h> -#include <clientversion.h> +#include <fs.h> +#include <hash.h> +#include <iostream> #include <map> #include <set> #include <stdint.h> -#include <vector> -#include <iostream> #include <streams.h> -#include <fs.h> -#include <hash.h> - +#include <vector> /** * Extended statistics about a CAddress diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp index 60a4fbf0ba..a2fdab5609 100644 --- a/src/bench/examples.cpp +++ b/src/bench/examples.cpp @@ -10,7 +10,7 @@ static void Sleep100ms(benchmark::State& state) { while (state.KeepRunning()) { - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index c085095a2b..6982eaab61 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -524,7 +524,7 @@ static int CommandLineRPC(int argc, char *argv[]) } catch (const CConnectionFailed&) { if (fWait) - MilliSleep(1000); + UninterruptibleSleep(std::chrono::milliseconds{1000}); else throw; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 4b5cea4849..e284dce0d5 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -29,7 +29,7 @@ static void WaitForShutdown(NodeContext& node) { while (!ShutdownRequested()) { - MilliSleep(200); + UninterruptibleSleep(std::chrono::milliseconds{200}); } Interrupt(node); } diff --git a/src/blockencodings.h b/src/blockencodings.h index 55ed8989bb..be50166cfc 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -10,18 +10,29 @@ class CTxMemPool; -// Dumb helper to handle CTransaction compression at serialize-time -struct TransactionCompressor { -private: - CTransactionRef& tx; -public: - explicit TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {} +// Transaction compression schemes for compact block relay can be introduced by writing +// an actual formatter here. +using TransactionCompression = DefaultFormatter; - ADD_SERIALIZE_METHODS; +class DifferenceFormatter +{ + uint64_t m_shift = 0; - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(tx); //TODO: Compress tx encoding +public: + template<typename Stream, typename I> + void Ser(Stream& s, I v) + { + if (v < m_shift || v >= std::numeric_limits<uint64_t>::max()) throw std::ios_base::failure("differential value overflow"); + WriteCompactSize(s, v - m_shift); + m_shift = uint64_t(v) + 1; + } + template<typename Stream, typename I> + void Unser(Stream& s, I& v) + { + uint64_t n = ReadCompactSize(s); + m_shift += n; + if (m_shift < n || m_shift >= std::numeric_limits<uint64_t>::max() || m_shift < std::numeric_limits<I>::min() || m_shift > std::numeric_limits<I>::max()) throw std::ios_base::failure("differential value overflow"); + v = I(m_shift++); } }; @@ -31,39 +42,9 @@ public: uint256 blockhash; std::vector<uint16_t> indexes; - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(blockhash); - uint64_t indexes_size = (uint64_t)indexes.size(); - READWRITE(COMPACTSIZE(indexes_size)); - if (ser_action.ForRead()) { - size_t i = 0; - while (indexes.size() < indexes_size) { - indexes.resize(std::min((uint64_t)(1000 + indexes.size()), indexes_size)); - for (; i < indexes.size(); i++) { - uint64_t index = 0; - READWRITE(COMPACTSIZE(index)); - if (index > std::numeric_limits<uint16_t>::max()) - throw std::ios_base::failure("index overflowed 16 bits"); - indexes[i] = index; - } - } - - int32_t offset = 0; - for (size_t j = 0; j < indexes.size(); j++) { - if (int32_t(indexes[j]) + offset > std::numeric_limits<uint16_t>::max()) - throw std::ios_base::failure("indexes overflowed 16 bits"); - indexes[j] = indexes[j] + offset; - offset = int32_t(indexes[j]) + 1; - } - } else { - for (size_t i = 0; i < indexes.size(); i++) { - uint64_t index = indexes[i] - (i == 0 ? 0 : (indexes[i - 1] + 1)); - READWRITE(COMPACTSIZE(index)); - } - } + SERIALIZE_METHODS(BlockTransactionsRequest, obj) + { + READWRITE(obj.blockhash, Using<VectorFormatter<DifferenceFormatter>>(obj.indexes)); } }; @@ -77,24 +58,9 @@ public: explicit BlockTransactions(const BlockTransactionsRequest& req) : blockhash(req.blockhash), txn(req.indexes.size()) {} - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(blockhash); - uint64_t txn_size = (uint64_t)txn.size(); - READWRITE(COMPACTSIZE(txn_size)); - if (ser_action.ForRead()) { - size_t i = 0; - while (txn.size() < txn_size) { - txn.resize(std::min((uint64_t)(1000 + txn.size()), txn_size)); - for (; i < txn.size(); i++) - READWRITE(TransactionCompressor(txn[i])); - } - } else { - for (size_t i = 0; i < txn.size(); i++) - READWRITE(TransactionCompressor(txn[i])); - } + SERIALIZE_METHODS(BlockTransactions, obj) + { + READWRITE(obj.blockhash, Using<VectorFormatter<TransactionCompression>>(obj.txn)); } }; @@ -105,17 +71,7 @@ struct PrefilledTransaction { uint16_t index; CTransactionRef tx; - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - uint64_t idx = index; - READWRITE(COMPACTSIZE(idx)); - if (idx > std::numeric_limits<uint16_t>::max()) - throw std::ios_base::failure("index overflowed 16-bits"); - index = idx; - READWRITE(TransactionCompressor(tx)); - } + SERIALIZE_METHODS(PrefilledTransaction, obj) { READWRITE(COMPACTSIZE(obj.index), Using<TransactionCompression>(obj.tx)); } }; typedef enum ReadStatus_t @@ -153,43 +109,15 @@ public: size_t BlockTxCount() const { return shorttxids.size() + prefilledtxn.size(); } - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(header); - READWRITE(nonce); - - uint64_t shorttxids_size = (uint64_t)shorttxids.size(); - READWRITE(COMPACTSIZE(shorttxids_size)); + SERIALIZE_METHODS(CBlockHeaderAndShortTxIDs, obj) + { + READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn); if (ser_action.ForRead()) { - size_t i = 0; - while (shorttxids.size() < shorttxids_size) { - shorttxids.resize(std::min((uint64_t)(1000 + shorttxids.size()), shorttxids_size)); - for (; i < shorttxids.size(); i++) { - uint32_t lsb = 0; uint16_t msb = 0; - READWRITE(lsb); - READWRITE(msb); - shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb); - static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids serialization assumes 6-byte shorttxids"); - } - } - } else { - for (size_t i = 0; i < shorttxids.size(); i++) { - uint32_t lsb = shorttxids[i] & 0xffffffff; - uint16_t msb = (shorttxids[i] >> 32) & 0xffff; - READWRITE(lsb); - READWRITE(msb); + if (obj.BlockTxCount() > std::numeric_limits<uint16_t>::max()) { + throw std::ios_base::failure("indexes overflowed 16 bits"); } + obj.FillShortTxIDSelector(); } - - READWRITE(prefilledtxn); - - if (BlockTxCount() > std::numeric_limits<uint16_t>::max()) - throw std::ios_base::failure("indexes overflowed 16 bits"); - - if (ser_action.ForRead()) - FillShortTxIDSelector(); } }; diff --git a/src/compressor.h b/src/compressor.h index 7bb60d311e..223603e7e9 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -15,7 +15,17 @@ bool CompressScript(const CScript& script, std::vector<unsigned char> &out); unsigned int GetSpecialScriptSize(unsigned int nSize); bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &out); +/** + * Compress amount. + * + * nAmount is of type uint64_t and thus cannot be negative. If you're passing in + * a CAmount (int64_t), make sure to properly handle the case where the amount + * is negative before calling CompressAmount(...). + * + * @pre Function defined only for 0 <= nAmount <= MAX_MONEY. + */ uint64_t CompressAmount(uint64_t nAmount); + uint64_t DecompressAmount(uint64_t nAmount); /** Compact serializer for scripts. diff --git a/src/httprpc.cpp b/src/httprpc.cpp index ff75789223..4d49736140 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -174,7 +174,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) /* Deter brute-forcing If this results in a DoS the user really shouldn't have their RPC port exposed. */ - MilliSleep(250); + UninterruptibleSleep(std::chrono::milliseconds{250}); req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); req->WriteReply(HTTP_UNAUTHORIZED); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0e13b85806..11d73b7c9a 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -236,7 +236,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg) if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n", hreq->GetPeer().ToString()); - hreq->WriteReply(HTTP_BADMETHOD); + hreq->WriteReply(HTTP_BAD_METHOD); return; } @@ -268,10 +268,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg) item.release(); /* if true, queue took ownership */ else { LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n"); - item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded"); + item->req->WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Work queue depth exceeded"); } } else { - hreq->WriteReply(HTTP_NOTFOUND); + hreq->WriteReply(HTTP_NOT_FOUND); } } @@ -519,7 +519,7 @@ HTTPRequest::~HTTPRequest() if (!replySent) { // Keep track of whether reply was sent to avoid request leaks LogPrintf("%s: Unhandled request\n", __func__); - WriteReply(HTTP_INTERNAL, "Unhandled request"); + WriteReply(HTTP_INTERNAL_SERVER_ERROR, "Unhandled request"); } // evhttpd cleans up the request, as long as a reply was sent. } diff --git a/src/indirectmap.h b/src/indirectmap.h index 76da4a6bd5..417d500bd4 100644 --- a/src/indirectmap.h +++ b/src/indirectmap.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_INDIRECTMAP_H #define BITCOIN_INDIRECTMAP_H +#include <map> + template <class T> struct DereferencingComparator { bool operator()(const T a, const T b) const { return *a < *b; } }; diff --git a/src/init.cpp b/src/init.cpp index 1c9faec803..a637aac4d2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -47,11 +47,11 @@ #include <txdb.h> #include <txmempool.h> #include <ui_interface.h> +#include <util/asmap.h> #include <util/moneystr.h> #include <util/system.h> #include <util/threadnames.h> #include <util/translation.h> -#include <util/asmap.h> #include <validation.h> #include <hash.h> @@ -206,6 +206,7 @@ void Shutdown(NodeContext& node) // After everything has been shut down, but before things get flushed, stop the // CScheduler/checkqueue threadGroup + if (node.scheduler) node.scheduler->stop(); threadGroup.interrupt_all(); threadGroup.join_all(); @@ -408,6 +409,7 @@ void SetupServerArgs() ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + gArgs.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); @@ -436,7 +438,6 @@ void SetupServerArgs() gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION); - gArgs.AddArg("-asmap=<file>", "Specify asn mapping used for bucketing of the peers. Path should be relative to the -datadir path.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); #ifdef USE_UPNP #if USE_UPNP gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -1418,6 +1419,31 @@ bool AppInitMain(NodeContext& node) return InitError(ResolveErrMsg("externalip", strAddr)); } + // Read asmap file if configured + if (gArgs.IsArgSet("-asmap")) { + fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", "")); + if (asmap_path.empty()) { + asmap_path = DEFAULT_ASMAP_FILENAME; + } + if (!asmap_path.is_absolute()) { + asmap_path = GetDataDir() / asmap_path; + } + if (!fs::exists(asmap_path)) { + InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path)); + return false; + } + std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path); + if (asmap.size() == 0) { + InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path)); + return false; + } + const uint256 asmap_version = SerializeHash(asmap); + node.connman->SetAsmap(std::move(asmap)); + LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString()); + } else { + LogPrintf("Using /16 prefix for IP bucketing\n"); + } + #if ENABLE_ZMQ g_zmq_notification_interface = CZMQNotificationInterface::Create(); @@ -1825,25 +1851,6 @@ bool AppInitMain(NodeContext& node) return false; } - // Read asmap file if configured - if (gArgs.IsArgSet("-asmap")) { - std::string asmap_file = gArgs.GetArg("-asmap", ""); - if (asmap_file.empty()) { - asmap_file = DEFAULT_ASMAP_FILENAME; - } - const fs::path asmap_path = GetDataDir() / asmap_file; - std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path); - if (asmap.size() == 0) { - InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path)); - return false; - } - const uint256 asmap_version = SerializeHash(asmap); - node.connman->SetAsmap(std::move(asmap)); - LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString()); - } else { - LogPrintf("Using /16 prefix for IP bucketing.\n"); - } - // ********************************************************* Step 13: finished SetRPCWarmupFinished(); diff --git a/src/logging/timer.h b/src/logging/timer.h index 45bfc4aa65..2b27c71080 100644 --- a/src/logging/timer.h +++ b/src/logging/timer.h @@ -85,7 +85,7 @@ private: const std::string m_title{}; //! Forwarded on to LogPrint if specified - has the effect of only - //! outputing the timing log when a particular debug= category is specified. + //! outputting the timing log when a particular debug= category is specified. const BCLog::LogFlags m_log_category{}; }; diff --git a/src/memusage.h b/src/memusage.h index 3ae9face15..24eb450465 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -6,9 +6,11 @@ #define BITCOIN_MEMUSAGE_H #include <indirectmap.h> +#include <prevector.h> #include <stdlib.h> +#include <cassert> #include <map> #include <memory> #include <set> diff --git a/src/net.cpp b/src/net.cpp index cb4067e825..d156450394 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -555,9 +555,9 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap) } // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) - stats.dPingTime = (((double)nPingUsecTime) / 1e6); - stats.dMinPing = (((double)nMinPingUsecTime) / 1e6); - stats.dPingWait = (((double)nPingUsecWait) / 1e6); + stats.m_ping_usec = nPingUsecTime; + stats.m_min_ping_usec = nMinPingUsecTime; + stats.m_ping_wait_usec = nPingUsecWait; // Leave string empty if addrLocal invalid (not filled in yet) CService addrLocalUnlocked = GetAddrLocal(); @@ -596,9 +596,9 @@ public: mapMsgCmdSize mapRecvBytesPerMsgCmd; NetPermissionFlags m_permissionFlags; bool m_legacyWhitelisted; - double dPingTime; - double dPingWait; - double dMinPing; + int64_t m_ping_usec; + int64_t m_ping_wait_usec; + int64_t m_min_ping_usec; CAmount minFeeFilter; // Our address, as reported by the peer std::string addrLocal; diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 1cac57a817..228caf74a9 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -210,6 +210,11 @@ bool CNetAddr::IsRFC7343() const return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x20); } +bool CNetAddr::IsHeNet() const +{ + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70); +} + /** * @returns Whether or not this is a dummy address that maps an onion address * into IPv6. @@ -516,7 +521,7 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co } else if (IsTor()) { nStartByte = 6; nBits = 4; - } else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) { + } else if (IsHeNet()) { // for he.net, use /36 groups nBits = 36; } else { diff --git a/src/netaddress.h b/src/netaddress.h index b300b709f3..b7381c1eb4 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -45,7 +45,6 @@ class CNetAddr */ void SetRaw(Network network, const uint8_t *data); - public: bool SetInternal(const std::string& name); bool SetSpecial(const std::string &strName); // for Tor addresses @@ -66,6 +65,7 @@ class CNetAddr bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96) bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765) + bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36) bool IsTor() const; bool IsLocal() const; bool IsRoutable() const; diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index 8678b33cf3..5b16035f7d 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -18,9 +18,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) PSBTAnalysis result; bool calc_fee = true; - bool all_final = true; - bool only_missing_sigs = true; - bool only_missing_final = false; + CAmount in_amt = 0; result.inputs.resize(psbtx.tx->vin.size()); @@ -29,6 +27,9 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) PSBTInput& input = psbtx.inputs[i]; PSBTInputAnalysis& input_analysis = result.inputs[i]; + // We set next role here and ratchet backwards as required + input_analysis.next = PSBTRole::EXTRACTOR; + // Check for a UTXO CTxOut utxo; if (psbtx.GetInputUTXO(utxo, i)) { @@ -57,7 +58,6 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) // Check if it is final if (!utxo.IsNull() && !PSBTInputSigned(input)) { input_analysis.is_final = false; - all_final = false; // Figure out what is missing SignatureData outdata; @@ -74,11 +74,9 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) { input_analysis.next = PSBTRole::SIGNER; } else { - only_missing_sigs = false; input_analysis.next = PSBTRole::UPDATER; } } else { - only_missing_final = true; input_analysis.next = PSBTRole::FINALIZER; } } else if (!utxo.IsNull()){ @@ -86,10 +84,14 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) } } - if (all_final) { - only_missing_sigs = false; - result.next = PSBTRole::EXTRACTOR; + // Calculate next role for PSBT by grabbing "minimum" PSBTInput next role + result.next = PSBTRole::EXTRACTOR; + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + PSBTInputAnalysis& input_analysis = result.inputs[i]; + result.next = std::min(result.next, input_analysis.next); } + assert(result.next > PSBTRole::CREATOR); + if (calc_fee) { // Get the output amount CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0), @@ -139,17 +141,6 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) result.estimated_feerate = feerate; } - if (only_missing_sigs) { - result.next = PSBTRole::SIGNER; - } else if (only_missing_final) { - result.next = PSBTRole::FINALIZER; - } else if (all_final) { - result.next = PSBTRole::EXTRACTOR; - } else { - result.next = PSBTRole::UPDATER; - } - } else { - result.next = PSBTRole::UPDATER; } return result; diff --git a/src/prevector.h b/src/prevector.h index 09debedc4f..6d690e7f96 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -424,15 +424,20 @@ public: return first; } - void push_back(const T& value) { + template<typename... Args> + void emplace_back(Args&&... args) { size_type new_size = size() + 1; if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - new(item_ptr(size())) T(value); + new(item_ptr(size())) T(std::forward<Args>(args)...); _size++; } + void push_back(const T& value) { + emplace_back(value); + } + void pop_back() { erase(end() - 1, end()); } diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 911322092c..98dde1656a 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -773,9 +773,9 @@ QString formatServicesStr(quint64 mask) return QObject::tr("None"); } -QString formatPingTime(double dPingTime) +QString formatPingTime(int64_t ping_usec) { - return (dPingTime == std::numeric_limits<int64_t>::max()/1e6 || dPingTime == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10)); + return (ping_usec == std::numeric_limits<int64_t>::max() || ping_usec == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(ping_usec / 1000), 10)); } QString formatTimeOffset(int64_t nTimeOffset) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 05e73cc5f0..e571262443 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -202,8 +202,8 @@ namespace GUIUtil /* Format CNodeStats.nServices bitmask into a user-readable string */ QString formatServicesStr(quint64 mask); - /* Format a CNodeCombinedStats.dPingTime into a user-readable string or display N/A, if 0*/ - QString formatPingTime(double dPingTime); + /* Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0*/ + QString formatPingTime(int64_t ping_usec); /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ QString formatTimeOffset(int64_t nTimeOffset); diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index a497f58b16..a1fc791536 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -32,7 +32,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: - return pLeft->dMinPing < pRight->dMinPing; + return pLeft->m_min_ping_usec < pRight->m_min_ping_usec; case PeerTableModel::Sent: return pLeft->nSendBytes < pRight->nSendBytes; case PeerTableModel::Received: @@ -161,7 +161,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: - return GUIUtil::formatPingTime(rec->nodeStats.dMinPing); + return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec); case Sent: return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); case Received: diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index e1f783b0e5..b82ab9ffe8 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1109,9 +1109,9 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes)); ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes)); ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected)); - ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); - ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait)); - ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing)); + ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_usec)); + ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_wait_usec)); + ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_usec)); ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion))); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); diff --git a/src/reverselock.h b/src/reverselock.h deleted file mode 100644 index 9d9cc9fd77..0000000000 --- a/src/reverselock.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2015-2016 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_REVERSELOCK_H -#define BITCOIN_REVERSELOCK_H - -/** - * An RAII-style reverse lock. Unlocks on construction and locks on destruction. - */ -template<typename Lock> -class reverse_lock -{ -public: - - explicit reverse_lock(Lock& _lock) : lock(_lock) { - _lock.unlock(); - _lock.swap(templock); - } - - ~reverse_lock() { - templock.lock(); - templock.swap(lock); - } - -private: - reverse_lock(reverse_lock const&); - reverse_lock& operator=(reverse_lock const&); - - Lock& lock; - Lock templock; -}; - -#endif // BITCOIN_REVERSELOCK_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 40637a552e..133d1b809f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -177,8 +177,7 @@ static UniValue getblockcount(const JSONRPCRequest& request) "The genesis block has height 0.\n", {}, RPCResult{ - "n (numeric) The current block count\n" - }, + RPCResult::Type::NUM, "", "The current block count"}, RPCExamples{ HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "") @@ -195,8 +194,7 @@ static UniValue getbestblockhash(const JSONRPCRequest& request) "\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n", {}, RPCResult{ - "\"hex\" (string) the block hash, hex-encoded\n" - }, + RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"}, RPCExamples{ HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "") @@ -226,11 +224,11 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, }, RPCResult{ - "{ (json object)\n" - " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (numeric) Block height\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, + {RPCResult::Type::NUM, "height", "Block height"}, + }}, RPCExamples{ HelpExampleCli("waitfornewblock", "1000") + HelpExampleRpc("waitfornewblock", "1000") @@ -266,11 +264,11 @@ static UniValue waitforblock(const JSONRPCRequest& request) {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, }, RPCResult{ - "{ (json object)\n" - " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (numeric) Block height\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, + {RPCResult::Type::NUM, "height", "Block height"}, + }}, RPCExamples{ HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") @@ -310,11 +308,11 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) {"timeout", RPCArg::Type::NUM, /* default */ "0", "Time in milliseconds to wait for a response. 0 indicates no timeout."}, }, RPCResult{ - "{ (json object)\n" - " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (numeric) Block height\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, + {RPCResult::Type::NUM, "height", "Block height"}, + }}, RPCExamples{ HelpExampleCli("waitforblockheight", "\"100\", 1000") + HelpExampleRpc("waitforblockheight", "\"100\", 1000") @@ -364,8 +362,7 @@ static UniValue getdifficulty(const JSONRPCRequest& request) "\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" - }, + RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."}, RPCExamples{ HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "") @@ -376,37 +373,35 @@ static UniValue getdifficulty(const JSONRPCRequest& request) return GetDifficulty(::ChainActive().Tip()); } -static std::string EntryDescriptionString() -{ - return " \"vsize\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n" - " \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n" - " size will be completely removed in v0.20.\n" - " \"weight\" : n, (numeric) transaction weight as defined in BIP 141.\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n" - " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" - " \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)\n" - " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n" - " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n" - " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)\n" - " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" - " \"fees\" : {\n" - " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n" - " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n" - " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n" - " }\n" - " \"depends\" : [ (json array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " \"spentby\" : [ (json array) unconfirmed transactions spending outputs from this transaction\n" - " \"transactionid\", (string) child transaction id\n" - " ... ]\n" - " \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n"; -} +static std::vector<RPCResult> MempoolEntryDescription() { return { + RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."}, + RPCResult{RPCResult::Type::NUM, "size", "(DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n" + "size will be completely removed in v0.20."}, + RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."}, + RPCResult{RPCResult::Type::STR_AMOUNT, "fee", "transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", "transaction fee with fee deltas used for mining priority (DEPRECATED)"}, + RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"}, + RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"}, + RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"}, + RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", "modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)"}, + RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"}, + RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", "modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"}, + RPCResult{RPCResult::Type::OBJ, "fees", "", + { + RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT}, + }}, + RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction", + {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}}, + RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction", + {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}}, + RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"}, +};} static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs) { @@ -505,17 +500,17 @@ static UniValue getrawmempool(const JSONRPCRequest& request) { {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"}, }, - RPCResult{"for verbose = false", - "[ (json array of string)\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - "]\n" - "\nResult: (for verbose = true):\n" - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - + EntryDescriptionString() - + " }, ...\n" - "}\n" + { + RPCResult{"for verbose = false", + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR_HEX, "", "The transaction id"}, + }}, + RPCResult{"for verbose = true", + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()}, + }}, }, RPCExamples{ HelpExampleCli("getrawmempool", "true") @@ -540,18 +535,10 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) }, { RPCResult{"for verbose = false", - "[ (json array of strings)\n" - " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n" - " ,...\n" - "]\n" - }, + RPCResult::Type::ARR, "", "", + {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}}, RPCResult{"for verbose = true", - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - + EntryDescriptionString() - + " }, ...\n" - "}\n" - }, + RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()}, }, RPCExamples{ HelpExampleCli("getmempoolancestors", "\"mytxid\"") @@ -608,18 +595,13 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) }, { RPCResult{"for verbose = false", - "[ (json array of strings)\n" - " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n" - " ,...\n" - "]\n" - }, + RPCResult::Type::ARR, "", "", + {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}}, RPCResult{"for verbose = true", - "{ (json object)\n" - " \"transactionid\" : { (json object)\n" - + EntryDescriptionString() - + " }, ...\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ_DYN, "transactionid", "", MempoolEntryDescription()}, + }}, }, RPCExamples{ HelpExampleCli("getmempooldescendants", "\"mytxid\"") @@ -674,10 +656,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"}, }, RPCResult{ - "{ (json object)\n" - + EntryDescriptionString() - + "}\n" - }, + RPCResult::Type::OBJ_DYN, "", "", MempoolEntryDescription()}, RPCExamples{ HelpExampleCli("getmempoolentry", "\"mytxid\"") + HelpExampleRpc("getmempoolentry", "\"mytxid\"") @@ -708,8 +687,7 @@ static UniValue getblockhash(const JSONRPCRequest& request) {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"}, }, RPCResult{ - "\"hash\" (string) The block hash\n" - }, + RPCResult::Type::STR_HEX, "", "The block hash"}, RPCExamples{ HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000") @@ -737,27 +715,26 @@ static UniValue getblockheader(const JSONRPCRequest& request) }, { 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" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" - " \"nTx\" : n, (numeric) The number of transactions in the block.\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"}, + {RPCResult::Type::NUM, "height", "The block height or index"}, + {RPCResult::Type::NUM, "version", "The block version"}, + {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, + {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "nonce", "The nonce"}, + {RPCResult::Type::STR_HEX, "bits", "The bits"}, + {RPCResult::Type::NUM, "difficulty", "The difficulty"}, + {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"}, + {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"}, + {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"}, + {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"}, + }}, RPCResult{"for verbose=false", - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - }, + RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}, }, RPCExamples{ HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") @@ -839,44 +816,45 @@ static UniValue getblock(const JSONRPCRequest& request) }, { RPCResult{"for verbosity = 0", - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" - }, + RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}, 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" - " \"size\" : n, (numeric) The block size\n" - " \"strippedsize\" : n, (numeric) The block size excluding witness data\n" - " \"weight\" : n (numeric) The block weight as defined in BIP 141\n" - " \"height\" : n, (numeric) The block height or index\n" - " \"version\" : n, (numeric) The block version\n" - " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" - " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"tx\" : [ (array of string) The transaction ids\n" - " \"transactionid\" (string) The transaction id\n" - " ,...\n" - " ],\n" - " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"nonce\" : n, (numeric) The nonce\n" - " \"bits\" : \"1d00ffff\", (string) The bits\n" - " \"difficulty\" : x.xxx, (numeric) The difficulty\n" - " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n" - " \"nTx\" : n, (numeric) The number of transactions in the block.\n" - " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" - " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"}, + {RPCResult::Type::NUM, "size", "The block size"}, + {RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"}, + {RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"}, + {RPCResult::Type::NUM, "height", "The block height or index"}, + {RPCResult::Type::NUM, "version", "The block version"}, + {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, + {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, + {RPCResult::Type::ARR, "tx", "The transaction ids", + {{RPCResult::Type::STR_HEX, "", "The transaction id"}}}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "nonce", "The nonce"}, + {RPCResult::Type::STR_HEX, "bits", "The bits"}, + {RPCResult::Type::NUM, "difficulty", "The difficulty"}, + {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"}, + {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"}, + {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"}, + {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"}, + }}, 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" - " ,...\n" - " ],\n" - " ,... Same output as verbosity = 1.\n" - "}\n" - }, - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"}, + {RPCResult::Type::ARR, "tx", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"}, + }}, + }}, + {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"}, + }}, + }, RPCExamples{ HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") @@ -927,8 +905,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) " to prune blocks whose block time is at least 2 hours older than the provided timestamp."}, }, RPCResult{ - "n (numeric) Height of the last block pruned.\n" - }, + RPCResult::Type::NUM, "", "Height of the last block pruned"}, RPCExamples{ HelpExampleCli("pruneblockchain", "1000") + HelpExampleRpc("pruneblockchain", "1000") @@ -982,17 +959,17 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) "Note this call may take some time.\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" - " \"transactions\" : n, (numeric) The number of transactions with unspent outputs\n" - " \"txouts\" : n, (numeric) The number of unspent transaction outputs\n" - " \"bogosize\" : n, (numeric) A meaningless metric for UTXO set size\n" - " \"hash_serialized_2\": \"hash\", (string) The serialized hash\n" - " \"disk_size\" : n, (numeric) The estimated size of the chainstate on disk\n" - " \"total_amount\" : x.xxx (numeric) The total amount\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "height", "The current block height (index)"}, + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"}, + {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"}, + {RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"}, + {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash"}, + {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"}, + {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"}, + }}, RPCExamples{ HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "") @@ -1030,23 +1007,22 @@ UniValue gettxout(const JSONRPCRequest& request) {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, }, 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" - " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"code\", (string) \n" - " \"hex\" : \"hex\", (string) \n" - " \"reqSigs\" : n, (numeric) Number of required signatures\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" - " \"addresses\" : [ (array of string) array of bitcoin addresses\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " },\n" - " \"coinbase\" : true|false (boolean) Coinbase or not\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations"}, + {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR_HEX, "asm", ""}, + {RPCResult::Type::STR_HEX, "hex", ""}, + {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"}, + {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"}, + {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses", + {{RPCResult::Type::STR, "address", "bitcoin address"}}}, + }}, + {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"}, + }}, RPCExamples{ "\nGet unspent transactions\n" + HelpExampleCli("listunspent", "") + @@ -1111,8 +1087,7 @@ static UniValue verifychain(const JSONRPCRequest& request) {"nblocks", RPCArg::Type::NUM, /* default */ strprintf("%d, 0=all", nCheckDepth), "The number of blocks to check."}, }, RPCResult{ - "true|false (boolean) Verified or not\n" - }, + RPCResult::Type::BOOL, "", "Verified or not"}, RPCExamples{ HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "") @@ -1202,45 +1177,49 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) "Returns an object containing various state info regarding blockchain processing.\n", {}, RPCResult{ - "{\n" - " \"chain\" : \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"blocks\" : xxxxxx, (numeric) the height of the most-work fully-validated chain. The genesis block has height 0\n" - " \"headers\" : xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\" : \"...\", (string) the hash of the currently best block\n" - " \"difficulty\" : xxxxxx, (numeric) the current difficulty\n" - " \"mediantime\" : xxxxxx, (numeric) median time for the current best block\n" - " \"verificationprogress\" : xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"initialblockdownload\" : xxxx, (boolean) (debug information) estimate of whether this node is in Initial Block Download mode.\n" - " \"chainwork\" : \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"size_on_disk\" : xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" - " \"pruned\" : xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\" : xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" - " \"automatic_pruning\" : xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" - " \"prune_target_size\" : xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" - " \"softforks\" : { (json object) status of softforks\n" - " \"xxxx\" : { (string) name of the softfork\n" - " \"type\" : \"xxxx\", (string) one of \"buried\", \"bip9\"\n" - " \"bip9\": { (json object) status of bip9 softforks (only for \"bip9\" type)\n" - " \"status\" : \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" - " \"bit\" : xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" - " \"start_time\" : xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" - " \"timeout\" : xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" - " \"since\" : xx, (numeric) height of the first block to which the status applies\n" - " \"statistics\" : { (json object) numeric statistics about BIP9 signalling for a softfork\n" - " \"period\" : xx, (numeric) the length in blocks of the BIP9 signalling period \n" - " \"threshold\" : xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" - " \"elapsed\" : xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" - " \"count\" : xx, (numeric) the number of blocks with the version bit set in the current period \n" - " \"possible\" : xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" - " }\n" - " },\n" - " \"height\" : \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n" - " \"active\" : xx, (boolean) true if the rules are enforced for the mempool and the next block\n" - " }\n" - " }\n" - " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"}, + {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"}, + {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"}, + {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"}, + {RPCResult::Type::NUM, "difficulty", "the current difficulty"}, + {RPCResult::Type::NUM, "mediantime", "median time for the current best block"}, + {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"}, + {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"}, + {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"}, + {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"}, + {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"}, + {RPCResult::Type::NUM, "pruneheight", "lowest-height complete block stored (only present if pruning is enabled)"}, + {RPCResult::Type::BOOL, "automatic_pruning", "whether automatic pruning is enabled (only present if pruning is enabled)"}, + {RPCResult::Type::NUM, "prune_target_size", "the target size used by pruning (only present if automatic pruning is enabled)"}, + {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks", + { + {RPCResult::Type::OBJ, "xxxx", "name of the softfork", + { + {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""}, + {RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)", + { + {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""}, + {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"}, + {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, + {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, + {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, + {RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork", + { + {RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"}, + {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature"}, + {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"}, + {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"}, + {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold"}, + }}, + }}, + {RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"}, + }}, + }}, + {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, + }}, RPCExamples{ HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "") @@ -1315,27 +1294,20 @@ static UniValue getchaintips(const JSONRPCRequest& request) " including the main chain as well as orphaned branches.\n", {}, RPCResult{ - "[\n" - " {\n" - " \"height\" : xxxx, (numeric) height of the chain tip\n" - " \"hash\" : \"xxxx\", (string) block hash of the tip\n" - " \"branchlen\" : 0 (numeric) zero for main chain\n" - " \"status\" : \"active\" (string) \"active\" for the main chain\n" - " },\n" - " {\n" - " \"height\" : xxxx,\n" - " \"hash\" : \"xxxx\",\n" - " \"branchlen\" : 1 (numeric) length of branch connecting the tip to the main chain\n" - " \"status\" : \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" - " }\n" - "]\n" + RPCResult::Type::ARR, "", "", + {{RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "height", "height of the chain tip"}, + {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"}, + {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"}, + {RPCResult::Type::STR, "status", "status of the chain, \"active\" for the main chain\n" "Possible values for status:\n" "1. \"invalid\" This branch contains at least one invalid block\n" "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" "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" - }, + "5. \"active\" This is the tip of the active main chain, which is certainly valid"}, + }}}}, RPCExamples{ HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "") @@ -1435,16 +1407,16 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) "\nReturns details on the active state of the TX memory pool.\n", {}, RPCResult{ - "{\n" - " \"loaded\" : true|false (boolean) True if the mempool is fully loaded\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" - " \"usage\" : xxxxx, (numeric) Total memory usage for the mempool\n" - " \"maxmempool\" : xxxxx, (numeric) Maximum memory usage for the mempool\n" - " \"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" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"}, + {RPCResult::Type::NUM, "size", "Current tx count"}, + {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"}, + {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"}, + {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"}, + {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"}, + {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"}, + }}, RPCExamples{ HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "") @@ -1575,17 +1547,17 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) {"blockhash", RPCArg::Type::STR_HEX, /* default */ "chain tip", "The hash of the block that ends the window."}, }, RPCResult{ - "{\n" - " \"time\" : xxxxx, (numeric) The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"txcount\" : xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" - " \"window_final_block_hash\" : \"...\", (string) The hash of the final block in the window.\n" - " \"window_final_block_height\" : xxxxx, (numeric) The height of the final block in the window.\n" - " \"window_block_count\" : xxxxx, (numeric) Size of the window in number of blocks.\n" - " \"window_tx_count\" : xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" - " \"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" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"}, + {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"}, + {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."}, + {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"}, + {RPCResult::Type::NUM, "window_tx_count", "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"}, + {RPCResult::Type::NUM, "window_interval", "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"}, + {RPCResult::Type::NUM, "txrate", "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"}, + }}, RPCExamples{ HelpExampleCli("getchaintxstats", "") + HelpExampleRpc("getchaintxstats", "2016") @@ -1714,44 +1686,45 @@ static UniValue getblockstats(const JSONRPCRequest& request) "stats"}, }, RPCResult{ - "{ (json object)\n" - " \"avgfee\" : xxxxx, (numeric) Average fee in the block\n" - " \"avgfeerate\" : xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n" - " \"avgtxsize\" : xxxxx, (numeric) Average transaction size\n" - " \"blockhash\" : xxxxx, (string) The block hash (to check for potential reorgs)\n" - " \"feerate_percentiles\" : [ (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n" - " \"10th_percentile_feerate\", (numeric) The 10th percentile feerate\n" - " \"25th_percentile_feerate\", (numeric) The 25th percentile feerate\n" - " \"50th_percentile_feerate\", (numeric) The 50th percentile feerate\n" - " \"75th_percentile_feerate\", (numeric) The 75th percentile feerate\n" - " \"90th_percentile_feerate\", (numeric) The 90th percentile feerate\n" - " ],\n" - " \"height\" : xxxxx, (numeric) The height of the block\n" - " \"ins\" : xxxxx, (numeric) The number of inputs (excluding coinbase)\n" - " \"maxfee\" : xxxxx, (numeric) Maximum fee in the block\n" - " \"maxfeerate\" : xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n" - " \"maxtxsize\" : xxxxx, (numeric) Maximum transaction size\n" - " \"medianfee\" : xxxxx, (numeric) Truncated median fee in the block\n" - " \"mediantime\" : xxxxx, (numeric) The block median time past\n" - " \"mediantxsize\" : xxxxx, (numeric) Truncated median transaction size\n" - " \"minfee\" : xxxxx, (numeric) Minimum fee in the block\n" - " \"minfeerate\" : xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n" - " \"mintxsize\" : xxxxx, (numeric) Minimum transaction size\n" - " \"outs\" : xxxxx, (numeric) The number of outputs\n" - " \"subsidy\" : xxxxx, (numeric) The block subsidy\n" - " \"swtotal_size\" : xxxxx, (numeric) Total size of all segwit transactions\n" - " \"swtotal_weight\" : xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n" - " \"swtxs\" : xxxxx, (numeric) The number of segwit transactions\n" - " \"time\" : xxxxx, (numeric) The block time\n" - " \"total_out\" : xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n" - " \"total_size\" : xxxxx, (numeric) Total size of all non-coinbase transactions\n" - " \"total_weight\" : xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n" - " \"totalfee\" : xxxxx, (numeric) The fee total\n" - " \"txs\" : xxxxx, (numeric) The number of transactions (excluding coinbase)\n" - " \"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" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "avgfee", "Average fee in the block"}, + {RPCResult::Type::NUM, "avgfeerate", "Average feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "avgtxsize", "Average transaction size"}, + {RPCResult::Type::STR_HEX, "blockhash", "The block hash (to check for potential reorgs)"}, + {RPCResult::Type::ARR_FIXED, "feerate_percentiles", "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)", + { + {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"}, + {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"}, + {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"}, + {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"}, + {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"}, + }}, + {RPCResult::Type::NUM, "height", "The height of the block"}, + {RPCResult::Type::NUM, "ins", "The number of inputs (excluding coinbase)"}, + {RPCResult::Type::NUM, "maxfee", "Maximum fee in the block"}, + {RPCResult::Type::NUM, "maxfeerate", "Maximum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "maxtxsize", "Maximum transaction size"}, + {RPCResult::Type::NUM, "medianfee", "Truncated median fee in the block"}, + {RPCResult::Type::NUM, "mediantime", "The block median time past"}, + {RPCResult::Type::NUM, "mediantxsize", "Truncated median transaction size"}, + {RPCResult::Type::NUM, "minfee", "Minimum fee in the block"}, + {RPCResult::Type::NUM, "minfeerate", "Minimum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "mintxsize", "Minimum transaction size"}, + {RPCResult::Type::NUM, "outs", "The number of outputs"}, + {RPCResult::Type::NUM, "subsidy", "The block subsidy"}, + {RPCResult::Type::NUM, "swtotal_size", "Total size of all segwit transactions"}, + {RPCResult::Type::NUM, "swtotal_weight", "Total weight of all segwit transactions divided by segwit scale factor (4)"}, + {RPCResult::Type::NUM, "swtxs", "The number of segwit transactions"}, + {RPCResult::Type::NUM, "time", "The block time"}, + {RPCResult::Type::NUM, "total_out", "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"}, + {RPCResult::Type::NUM, "total_size", "Total size of all non-coinbase transactions"}, + {RPCResult::Type::NUM, "total_weight", "Total weight of all non-coinbase transactions divided by segwit scale factor (4)"}, + {RPCResult::Type::NUM, "totalfee", "The fee total"}, + {RPCResult::Type::NUM, "txs", "The number of transactions (excluding coinbase)"}, + {RPCResult::Type::NUM, "utxo_increase", "The increase/decrease in the number of unspent outputs"}, + {RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"}, + }}, RPCExamples{ HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") + HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") @@ -2074,24 +2047,26 @@ UniValue scantxoutset(const JSONRPCRequest& request) "[scanobjects,...]"}, }, RPCResult{ - "{\n" - " \"success\" : true|false, (boolean) Whether the scan was completed\n" - " \"txouts\" : n, (numeric) The number of unspent transaction outputs scanned\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" - " \"unspents\" : [\n" - " {\n" - " \"txid\" : \"hash\", (string) The transaction id\n" - " \"vout\" : n, (numeric) The vout value\n" - " \"scriptPubKey\" : \"script\", (string) The script key\n" - " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n" - " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" - " \"height\" : n, (numeric) Height of the unspent transaction output\n" - " }\n" - " ,...],\n" - " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" - "]\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "success", "Whether the scan was completed"}, + {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"}, + {RPCResult::Type::NUM, "height", "The current block height (index)"}, + {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, + {RPCResult::Type::ARR, "unspents", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::NUM, "vout", "The vout value"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"}, + {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"}, + {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"}, + {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"}, + }}, + }}, + {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, + }}, RPCExamples{""}, }.Check(request); @@ -2197,11 +2172,11 @@ static UniValue getblockfilter(const JSONRPCRequest& request) {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"}, }, RPCResult{ - "{\n" - " \"filter\" : (string) the hex-encoded filter data\n" - " \"header\" : (string) the hex-encoded filter header\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"}, + {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"}, + }}, RPCExamples{ HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"") } @@ -2282,12 +2257,13 @@ UniValue dumptxoutset(const JSONRPCRequest& request) "path to the output file. If relative, will be prefixed by datadir."}, }, RPCResult{ - "{\n" - " \"coins_written\" : n, (numeric) the number of coins written in the snapshot\n" - " \"base_hash\" : \"...\", (string) the hash of the base of the snapshot\n" - " \"base_height\" : n, (string) the height of the base of the snapshot\n" - " \"path\" : \"...\" (string) the absolute path that the snapshot was written to\n" - "]\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"}, + {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"}, + {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"}, + {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"}, + } }, RPCExamples{ HelpExampleCli("dumptxoutset", "utxo.dat") diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 5bfdcd6555..9245d09b89 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -89,8 +89,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request) {"height", RPCArg::Type::NUM, /* default */ "-1", "To estimate at the time of the given height."}, }, RPCResult{ - "x (numeric) Hashes per second estimated\n" - }, + RPCResult::Type::NUM, "", "Hashes per second estimated"}, RPCExamples{ HelpExampleCli("getnetworkhashps", "") + HelpExampleRpc("getnetworkhashps", "") @@ -153,7 +152,11 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request) {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."}, }, RPCResult{ - "[ blockhashes ] (json array) hashes of blocks generated\n"}, + RPCResult::Type::ARR, "", "hashes of blocks generated", + { + {RPCResult::Type::STR_HEX, "", "blockhash"}, + } + }, RPCExamples{ "\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")}, } @@ -195,8 +198,10 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."}, }, RPCResult{ - "[ blockhashes ] (json array) hashes of blocks generated\n" - }, + RPCResult::Type::ARR, "", "hashes of blocks generated", + { + {RPCResult::Type::STR_HEX, "", "blockhash"}, + }}, RPCExamples{ "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") @@ -229,17 +234,17 @@ static UniValue getmininginfo(const JSONRPCRequest& request) "\nReturns a json object containing mining-related information.", {}, RPCResult{ - "{\n" - " \"blocks\" : nnn, (numeric) The current block\n" - " \"currentblockweight\" : nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n" - " \"currentblocktx\" : nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n" - " \"difficulty\" : xxx.xxxxx (numeric) The current difficulty\n" - " \"networkhashps\" : nnn, (numeric) The network hashes per second\n" - " \"pooledtx\" : n (numeric) The size of the mempool\n" - " \"chain\" : \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"warnings\" : \"...\" (string) any network and blockchain warnings\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "blocks", "The current block"}, + {RPCResult::Type::NUM, "currentblockweight", /* optional */ true, "The block weight of the last assembled block (only present if a block was ever assembled)"}, + {RPCResult::Type::NUM, "currentblocktx", /* optional */ true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"}, + {RPCResult::Type::NUM, "difficulty", "The current difficulty"}, + {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"}, + {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"}, + {RPCResult::Type::STR, "chain", "current network name (main, test, regtest)"}, + {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, + }}, RPCExamples{ HelpExampleCli("getmininginfo", "") + HelpExampleRpc("getmininginfo", "") @@ -277,8 +282,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) " considers the transaction as it would have paid a higher (or lower) fee."}, }, RPCResult{ - "true (boolean) Returns true\n" - }, + RPCResult::Type::BOOL, "", "Returns true"}, RPCExamples{ HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000") @@ -355,48 +359,58 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) "\"template_request\""}, }, RPCResult{ - "{\n" - " \"version\" : n, (numeric) The preferred block version\n" - " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" - " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n" - " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" - " ,...\n" - " },\n" - " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" - " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" - " \"transactions\" : [ (json array) contents of non-coinbase transactions that should be included in the next block\n" - " {\n" - " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" - " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" - " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n" - " \"depends\" : [ (json array) array of numbers \n" - " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" - " ,...\n" - " ],\n" - " \"fee\" : n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" - " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" - " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" - " }\n" - " ,...\n" - " ],\n" - " \"coinbaseaux\" : { ... }, (json object) data that should be included in the coinbase's scriptSig content\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n" - " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" - " \"target\" : \"xxxx\", (string) The hash target\n" - " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME + "\n" - " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" - " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" - " ,...\n" - " ],\n" - " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid nonces\n" - " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" - " \"sizelimit\" : n, (numeric) limit of block size\n" - " \"weightlimit\" : n, (numeric) limit of block weight\n" - " \"curtime\" : ttt, (numeric) current timestamp in " + UNIX_EPOCH_TIME + "\n" - " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n" - " \"height\" : n (numeric) The height of the next block\n" - "}\n" - }, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "version", "The preferred block version"}, + {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced", + { + {RPCResult::Type::STR, "", "rulename"}, + }}, + {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments", + { + {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"}, + }}, + {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"}, + {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"}, + {RPCResult::Type::ARR, "", "contents of non-coinbase transactions that should be included in the next block", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"}, + {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"}, + {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"}, + {RPCResult::Type::ARR, "depends", "array of numbers", + { + {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"}, + }}, + {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"}, + {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"}, + {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"}, + }}, + }}, + {RPCResult::Type::OBJ, "coinbaseaux", "data that should be included in the coinbase's scriptSig content", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"}, + {RPCResult::Type::OBJ, "coinbasetxn", "information for coinbase transaction", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::STR, "target", "The hash target"}, + {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed", + { + {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"}, + }}, + {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"}, + {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, + {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, + {RPCResult::Type::NUM, "weightlimit", "limit of block weight"}, + {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::STR, "bits", "compressed target of next block"}, + {RPCResult::Type::NUM, "height", "The height of the next block"}, + }}, RPCExamples{ HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'") + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}") @@ -799,8 +813,7 @@ static UniValue submitheader(const JSONRPCRequest& request) {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block header data"}, }, RPCResult{ - "None" - }, + RPCResult::Type::NONE, "", "None"}, RPCExamples{ HelpExampleCli("submitheader", "\"aabbcc\"") + HelpExampleRpc("submitheader", "\"aabbcc\"") @@ -847,17 +860,19 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) " \"CONSERVATIVE\""}, }, 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" - " \"blocks\" : n (numeric) block number where estimate was found\n" - "}\n" - "\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB (only present if no errors were encountered)"}, + {RPCResult::Type::ARR, "errors", "Errors encountered during processing", + { + {RPCResult::Type::STR, "", "error"}, + }}, + {RPCResult::Type::NUM, "blocks", "block number where estimate was found\n" "The request target will be clamped between 2 and the highest target\n" "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" - }, + "have been observed to make an estimate for any number of blocks."}, + }}, RPCExamples{ HelpExampleCli("estimatesmartfee", "6") }, @@ -907,36 +922,40 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) " lower buckets."}, }, RPCResult{ - "{ (json object) Results are returned for any horizon which tracks blocks up to the confirmation target\n" - " \"short\" : { (json object, optional) estimate for short time horizon\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" - " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" - " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" - " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n" - " \"startrange\" : x.x, (numeric) start of feerate range\n" - " \"endrange\" : x.x, (numeric) end of feerate range\n" - " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n" - " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n" - " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n" - " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n" - " },\n" - " \"fail\" : { (json object, optional) information about the highest range of feerates to fail to meet the threshold\n" - " ...\n" - " },\n" - " \"errors\" : [ (json array, optional) Errors encountered during processing\n" - " \"str\", (string)\n" - " ...\n" - " ],\n" - " },\n" - " \"medium\" : { (json object, optional) estimate for medium time horizon\n" - " ...\n" - " },\n" - " \"long\" : { (json object, optional) estimate for long time horizon\n" - " ...\n" - " },\n" - "}\n" - "\n" - }, + RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target", + { + {RPCResult::Type::OBJ, "short", /* optional */ true, "estimate for short time horizon", + { + {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"}, + {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"}, + {RPCResult::Type::OBJ, "pass", /* optional */ true, "information about the lowest range of feerates to succeed in meeting the threshold", + { + {RPCResult::Type::NUM, "startrange", "start of feerate range"}, + {RPCResult::Type::NUM, "endrange", "end of feerate range"}, + {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"}, + {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"}, + {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"}, + {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"}, + }}, + {RPCResult::Type::OBJ, "fail", /* optional */ true, "information about the highest range of feerates to fail to meet the threshold", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing", + { + {RPCResult::Type::STR, "error", ""}, + }}, + }}, + {RPCResult::Type::OBJ, "medium", /* optional */ true, "estimate for medium time horizon", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + {RPCResult::Type::OBJ, "long", /* optional */ true, "estimate for long time horizon", + { + {RPCResult::Type::ELISION, "", ""}, + }}, + }}, RPCExamples{ HelpExampleCli("estimaterawfee", "6 0.9") }, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 4279756f4d..8357183934 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -33,15 +33,16 @@ static UniValue validateaddress(const JSONRPCRequest& request) {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, }, 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" - " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n" - " \"isscript\" : true|false, (boolean) If the key is a script\n" - " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" - " \"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" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not. If not, this is the only property returned."}, + {RPCResult::Type::STR, "address", "The bitcoin address validated"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"}, + {RPCResult::Type::BOOL, "isscript", "If the key is a script"}, + {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"}, + {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"}, + {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"}, + } }, RPCExamples{ HelpExampleCli("validateaddress", EXAMPLE_ADDRESS) + @@ -82,11 +83,12 @@ static UniValue createmultisig(const JSONRPCRequest& request) {"address_type", RPCArg::Type::STR, /* default */ "legacy", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, }, 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" - " \"descriptor\" : \"descriptor\" (string) The descriptor for this multisig\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The value of the new multisig address."}, + {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."}, + {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, + } }, RPCExamples{ "\nCreate a multisig address from 2 public keys\n" @@ -141,13 +143,14 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request) {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."}, }, RPCResult{ - "{\n" - " \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n" - " \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n" - " \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n" - " \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n" - " \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"}, + {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"}, + {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"}, + {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"}, + {RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"}, + } }, RPCExamples{ "Analyse a descriptor\n" + @@ -189,7 +192,10 @@ UniValue deriveaddresses(const JSONRPCRequest& request) {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."}, }, RPCResult{ - "[ address ] (json array) the derived addresses\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR, "address", "the derived addresses"}, + } }, RPCExamples{ "First three native segwit receive addresses\n" + @@ -258,7 +264,7 @@ static UniValue verifymessage(const JSONRPCRequest& request) {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."}, }, RPCResult{ - "true|false (boolean) If the signature is verified or not.\n" + RPCResult::Type::BOOL, "", "If the signature is verified or not." }, RPCExamples{ "\nUnlock the wallet for 30 seconds\n" @@ -304,7 +310,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request) {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."}, }, RPCResult{ - "\"signature\" (string) The signature of the message encoded in base 64\n" + RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64" }, RPCExamples{ "\nCreate the signature\n" @@ -345,8 +351,9 @@ static UniValue setmocktime(const JSONRPCRequest& request) RPCExamples{""}, }.Check(request); - if (!Params().MineBlocksOnDemand()) - throw std::runtime_error("setmocktime for regression testing (-regtest mode) only"); + if (!Params().IsMockableChain()) { + throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only"); + } // For now, don't change mocktime if we're in the middle of validation, as // this could have an effect on mempool time-based eviction, as well as @@ -386,7 +393,7 @@ static UniValue mockscheduler(const JSONRPCRequest& request) // protect against null pointer dereference CHECK_NONFATAL(g_rpc_node); CHECK_NONFATAL(g_rpc_node->scheduler); - g_rpc_node->scheduler->MockForward(boost::chrono::seconds(delta_seconds)); + g_rpc_node->scheduler->MockForward(std::chrono::seconds(delta_seconds)); return NullUniValue; } @@ -437,19 +444,21 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request) }, { RPCResult{"mode \"stats\"", - "{\n" - " \"locked\" : { (json object) Information about locked memory manager\n" - " \"used\" : xxxxx, (numeric) Number of bytes used\n" - " \"free\" : xxxxx, (numeric) Number of bytes available in current arenas\n" - " \"total\" : xxxxxxx, (numeric) Total number of bytes managed\n" - " \"locked\" : xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n" - " \"chunks_used\" : xxxxx, (numeric) Number allocated chunks\n" - " \"chunks_free\" : xxxxx, (numeric) Number unused chunks\n" - " }\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "locked", "Information about locked memory manager", + { + {RPCResult::Type::NUM, "used", "Number of bytes used"}, + {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"}, + {RPCResult::Type::NUM, "total", "Total number of bytes managed"}, + {RPCResult::Type::NUM, "locked", "Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk."}, + {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"}, + {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"}, + }}, + } }, RPCResult{"mode \"mallocinfo\"", - "\"<malloc version=\"1\">...\"\n" + RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\"" }, }, RPCExamples{ @@ -516,10 +525,10 @@ UniValue logging(const JSONRPCRequest& request) }}, }, RPCResult{ - "{ (json object where keys are the logging categories, and values indicates its status\n" - " \"category\" : true|false, (boolean) if being debug logged or not. false:inactive, true:active\n" - " ...\n" - "}\n" + RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status", + { + {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"}, + } }, RPCExamples{ HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 92542539df..eeb617f849 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -33,7 +33,7 @@ static UniValue getconnectioncount(const JSONRPCRequest& request) "\nReturns the number of connections to other nodes.\n", {}, RPCResult{ - "n (numeric) The connection count\n" + RPCResult::Type::NUM, "", "The connection count" }, RPCExamples{ HelpExampleCli("getconnectioncount", "") @@ -77,57 +77,60 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) "\nReturns data about each connected network node as a json array of objects.\n", {}, RPCResult{ - "[\n" - " {\n" - " \"id\" : n, (numeric) Peer index\n" - " \"addr\" : \"host:port\", (string) The IP address and port of the peer\n" - " \"addrbind\" : \"ip:port\", (string) Bind address of the connection to the peer\n" - " \"addrlocal\" : \"ip:port\", (string) Local address as reported by the peer\n" - " \"mapped_as\" : \"mapped_as\", (string) The AS in the BGP route to the peer used for diversifying peer selection\n" - " \"services\" : \"xxxxxxxxxxxxxxxx\", (string) The services offered\n" - " \"servicesnames\" : [ (json array) the services offered, in human-readable form\n" - " \"SERVICE_NAME\", (string) the service name if it is recognised\n" - " ...\n" - " ],\n" - " \"relaytxes\" : true|false, (boolean) Whether peer has asked us to relay transactions to it\n" - " \"lastsend\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last send\n" - " \"lastrecv\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last receive\n" - " \"bytessent\" : n, (numeric) The total bytes sent\n" - " \"bytesrecv\" : n, (numeric) The total bytes received\n" - " \"conntime\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the connection\n" - " \"timeoffset\" : ttt, (numeric) The time offset in seconds\n" - " \"pingtime\" : n, (numeric) ping time (if available)\n" - " \"minping\" : n, (numeric) minimum observed ping time (if any at all)\n" - " \"pingwait\" : n, (numeric) ping wait (if non-zero)\n" - " \"version\" : v, (numeric) The peer version, such as 70001\n" - " \"subver\" : \"/Satoshi:0.8.5/\", (string) The string version\n" - " \"inbound\" : true|false, (boolean) Inbound (true) or Outbound (false)\n" - " \"addnode\" : true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" - " \"startingheight\" : n, (numeric) The starting height (block) of the peer\n" - " \"banscore\" : n, (numeric) The ban score\n" - " \"synced_headers\" : n, (numeric) The last header we have in common with this peer\n" - " \"synced_blocks\" : n, (numeric) The last block we have in common with this peer\n" - " \"inflight\" : [\n" - " n, (numeric) The heights of blocks we're currently asking from this peer\n" - " ...\n" - " ],\n" - " \"whitelisted\" : true|false, (boolean) Whether the peer is whitelisted\n" - " \"minfeefilter\" : n, (numeric) The minimum fee rate for transactions this peer accepts\n" - " \"bytessent_per_msg\" : {\n" - " \"msg\" : n, (numeric) The total bytes sent aggregated by message type\n" - " When a message type is not listed in this json object, the bytes sent are 0.\n" - " Only known message types can appear as keys in the object.\n" - " ...\n" - " },\n" - " \"bytesrecv_per_msg\" : {\n" - " \"msg\" : n, (numeric) The total bytes received aggregated by message type\n" - " When a message type is not listed in this json object, the bytes received are 0.\n" - " Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'.\n" - " ...\n" - " }\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + { + {RPCResult::Type::NUM, "id", "Peer index"}, + {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, + {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"}, + {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"}, + {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n" + "peer selection (only available if the asmap config flag is set)"}, + {RPCResult::Type::STR_HEX, "services", "The services offered"}, + {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", + { + {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} + }}, + {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, + {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, + {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, + {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, + {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, + {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, + {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, + {RPCResult::Type::NUM, "pingtime", "ping time (if available)"}, + {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"}, + {RPCResult::Type::NUM, "pingwait", "ping wait (if non-zero)"}, + {RPCResult::Type::NUM, "version", "The peer version, such as 70001"}, + {RPCResult::Type::STR, "subver", "The string version"}, + {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, + {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"}, + {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"}, + {RPCResult::Type::NUM, "banscore", "The ban score"}, + {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"}, + {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"}, + {RPCResult::Type::ARR, "inflight", "", + { + {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, + }}, + {RPCResult::Type::BOOL, "whitelisted", "Whether the peer is whitelisted"}, + {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, + {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n" + "When a message type is not listed in this json object, the bytes sent are 0.\n" + "Only known message types can appear as keys in the object."} + }}, + {RPCResult::Type::OBJ, "bytesrecv_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n" + "When a message type is not listed in this json object, the bytes received are 0.\n" + "Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} + }}, + }}, + }}, }, RPCExamples{ HelpExampleCli("getpeerinfo", "") @@ -165,12 +168,15 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) obj.pushKV("bytesrecv", stats.nRecvBytes); obj.pushKV("conntime", stats.nTimeConnected); obj.pushKV("timeoffset", stats.nTimeOffset); - if (stats.dPingTime > 0.0) - obj.pushKV("pingtime", stats.dPingTime); - if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6) - obj.pushKV("minping", stats.dMinPing); - if (stats.dPingWait > 0.0) - obj.pushKV("pingwait", stats.dPingWait); + if (stats.m_ping_usec > 0) { + obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6); + } + if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) { + obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6); + } + if (stats.m_ping_wait_usec > 0) { + obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6); + } obj.pushKV("version", stats.nVersion); // Use the sanitized form of subver here, to avoid tricksy remote peers from // corrupting or modifying the JSON output by putting special characters in @@ -320,19 +326,22 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request) {"node", RPCArg::Type::STR, /* default */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."}, }, RPCResult{ - "[\n" - " {\n" - " \"addednode\" : \"192.168.0.201\", (string) The node IP address or name (as provided to addnode)\n" - " \"connected\" : true|false, (boolean) If connected\n" - " \"addresses\" : [ (list of objects) Only when connected = true\n" - " {\n" - " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server IP and port we're connected to\n" - " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" - " }\n" - " ]\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "addednode", "The node IP address or name (as provided to addnode)"}, + {RPCResult::Type::BOOL, "connected", "If connected"}, + {RPCResult::Type::ARR, "addresses", "Only when connected = true", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The bitcoin server IP and port we're connected to"}, + {RPCResult::Type::STR, "connected", "connection, inbound or outbound"}, + }}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") @@ -386,20 +395,21 @@ static UniValue getnettotals(const JSONRPCRequest& request) "and current time.\n", {}, RPCResult{ - "{\n" - " \"totalbytesrecv\" : n, (numeric) Total bytes received\n" - " \"totalbytessent\" : n, (numeric) Total bytes sent\n" - " \"timemillis\" : t, (numeric) Current UNIX time in milliseconds\n" - " \"uploadtarget\" : \n" - " {\n" - " \"timeframe\" : n, (numeric) Length of the measuring timeframe in seconds\n" - " \"target\" : n, (numeric) Target in bytes\n" - " \"target_reached\" : true|false, (boolean) True if target is reached\n" - " \"serve_historical_blocks\" : true|false, (boolean) True if serving historical blocks\n" - " \"bytes_left_in_cycle\" : t, (numeric) Bytes left in current time cycle\n" - " \"time_left_in_cycle\" : t (numeric) Seconds left in current time cycle\n" - " }\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"}, + {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"}, + {RPCResult::Type::NUM_TIME, "timemillis", "Current UNIX time in milliseconds"}, + {RPCResult::Type::OBJ, "uploadtarget", "", + { + {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"}, + {RPCResult::Type::NUM, "target", "Target in bytes"}, + {RPCResult::Type::BOOL, "target_reached", "True if target is reached"}, + {RPCResult::Type::BOOL, "serve_historical_blocks", "True if serving historical blocks"}, + {RPCResult::Type::NUM, "bytes_left_in_cycle", "Bytes left in current time cycle"}, + {RPCResult::Type::NUM, "time_left_in_cycle", "Seconds left in current time cycle"}, + }}, + } }, RPCExamples{ HelpExampleCli("getnettotals", "") @@ -452,41 +462,44 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) "Returns an object containing various state info regarding P2P networking.\n", {}, RPCResult{ - "{ (json object)\n" - " \"version\" : xxxxx, (numeric) the server version\n" - " \"subversion\" : \"str\", (string) the server subversion string\n" - " \"protocolversion\" : xxxxx, (numeric) the protocol version\n" - " \"localservices\" : \"hex\", (string) the services we offer to the network\n" - " \"localservicesnames\" : [ (json array) the services we offer to the network, in human-readable form\n" - " \"SERVICE_NAME\", (string) the service name\n" - " ...\n" - " ],\n" - " \"localrelay\" : true|false, (boolean) true if transaction relay is requested from peers\n" - " \"timeoffset\" : xxxxx, (numeric) the time offset\n" - " \"connections\" : xxxxx, (numeric) the number of connections\n" - " \"networkactive\" : true|false, (boolean) whether p2p networking is enabled\n" - " \"networks\" : [ (json array) information per network\n" - " { (json object)\n" - " \"name\" : \"str\", (string) network (ipv4, ipv6 or onion)\n" - " \"limited\" : true|false, (boolean) is the network limited using -onlynet?\n" - " \"reachable\" : true|false, (boolean) is the network reachable?\n" - " \"proxy\" : \"str\" (string) (\"host:port\") the proxy that is used for this network, or empty if none\n" - " \"proxy_randomize_credentials\" : true|false, (boolean) Whether randomized credentials are used\n" - " },\n" - " ...\n" - " ],\n" - " \"relayfee\" : x.xxxxxxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" - " \"incrementalfee\" : x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n" - " \"localaddresses\" : [ (json array) list of local addresses\n" - " { (json object)\n" - " \"address\" : \"xxxx\", (string) network address\n" - " \"port\" : xxx, (numeric) network port\n" - " \"score\" : xxx (numeric) relative score\n" - " },\n" - " ...\n" - " ],\n" - " \"warnings\" : \"str\", (string) any network and blockchain warnings\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "version", "the server version"}, + {RPCResult::Type::STR, "subversion", "the server subversion string"}, + {RPCResult::Type::NUM, "protocolversion", "the protocol version"}, + {RPCResult::Type::STR_HEX, "localservices", "the services we offer to the network"}, + {RPCResult::Type::ARR, "localservicesnames", "the services we offer to the network, in human-readable form", + { + {RPCResult::Type::STR, "SERVICE_NAME", "the service name"}, + }}, + {RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"}, + {RPCResult::Type::NUM, "timeoffset", "the time offset"}, + {RPCResult::Type::NUM, "connections", "the number of connections"}, + {RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"}, + {RPCResult::Type::ARR, "networks", "information per network", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"}, + {RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"}, + {RPCResult::Type::BOOL, "reachable", "is the network reachable?"}, + {RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"}, + {RPCResult::Type::BOOL, "proxy_randomize_credentials", "Whether randomized credentials are used"}, + }}, + }}, + {RPCResult::Type::NUM, "relayfee", "minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::NUM, "incrementalfee", "minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::ARR, "localaddresses", "list of local addresses", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "network address"}, + {RPCResult::Type::NUM, "port", "network port"}, + {RPCResult::Type::NUM, "score", "relative score"}, + }}, + }}, + {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, + } }, RPCExamples{ HelpExampleCli("getnetworkinfo", "") @@ -693,15 +706,16 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) {"count", RPCArg::Type::NUM, /* default */ "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."}, }, RPCResult{ - "[\n" - " {\n" - " \"time\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of when the node was last seen\n" - " \"services\" : n, (numeric) The services offered\n" - " \"address\" : \"host\", (string) The address of the node\n" - " \"port\" : n (numeric) The port of the node\n" - " }\n" - " ,....\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"}, + {RPCResult::Type::NUM, "services", "The services offered"}, + {RPCResult::Type::STR, "address", "The address of the node"}, + {RPCResult::Type::NUM, "port", "The port of the node"}, + }}, + } }, RPCExamples{ HelpExampleCli("getnodeaddresses", "8") diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index cd1c657c26..361cf08086 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -94,54 +94,62 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) }, { RPCResult{"if verbose is not set or set to false", - "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'" }, RPCResult{"if verbose is set to true", - "{\n" - " \"in_active_chain\" : b, (boolean) 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" - " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" - " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" - " \"size\" : n, (numeric) The serialized transaction size\n" - " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" - " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n" - " \"version\" : n, (numeric) The version\n" - " \"locktime\" : ttt, (numeric) The lock time\n" - " \"vin\" : [ (array of json objects)\n" - " {\n" - " \"txid\" : \"id\", (string) The transaction id\n" - " \"vout\" : n, (numeric) \n" - " \"scriptSig\" : { (json object) The script\n" - " \"asm\" : \"asm\", (string) asm\n" - " \"hex\" : \"hex\" (string) hex\n" - " },\n" - " \"sequence\" : n (numeric) The script sequence number\n" - " \"txinwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " }\n" - " ,...\n" - " ],\n" - " \"vout\" : [ (array of json objects)\n" - " {\n" - " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"n\" : n, (numeric) index\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) the asm\n" - " \"hex\" : \"hex\", (string) the hex\n" - " \"reqSigs\" : n, (numeric) The required sigs\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"addresses\" : [ (json array of string)\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - " \"blockhash\" : \"hash\", (string) the block hash\n" - " \"confirmations\" : n, (numeric) The confirmations\n" - " \"blocktime\" : ttt (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" - " \"time\" : ttt, (numeric) Same as \"blocktime\"\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "in_active_chain", "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"}, + {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"}, + {RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"}, + {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"}, + {RPCResult::Type::NUM, "size", "The serialized transaction size"}, + {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"}, + {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4-3 and vsize*4)"}, + {RPCResult::Type::NUM, "version", "The version"}, + {RPCResult::Type::NUM_TIME, "locktime", "The lock time"}, + {RPCResult::Type::ARR, "vin", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::STR, "vout", ""}, + {RPCResult::Type::OBJ, "scriptSig", "The script", + { + {RPCResult::Type::STR, "asm", "asm"}, + {RPCResult::Type::STR_HEX, "hex", "hex"}, + }}, + {RPCResult::Type::NUM, "sequence", "The script sequence number"}, + {RPCResult::Type::ARR, "txinwitness", "", + { + {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, + }}, + }}, + }}, + {RPCResult::Type::ARR, "vout", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::NUM, "n", "index"}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "the asm"}, + {RPCResult::Type::STR, "hex", "the hex"}, + {RPCResult::Type::NUM, "reqSigs", "The required sigs"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::ARR, "addresses", "", + { + {RPCResult::Type::STR, "address", "bitcoin address"}, + }}, + }}, + }}, + }}, + {RPCResult::Type::STR_HEX, "blockhash", "the block hash"}, + {RPCResult::Type::NUM, "confirmations", "The confirmations"}, + {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "time", "Same as \"blocktime\""}, + } }, }, RPCExamples{ @@ -230,7 +238,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"}, }, RPCResult{ - "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" + RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof." }, RPCExamples{""}, }.Check(request); @@ -315,7 +323,10 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded proof generated by gettxoutproof"}, }, RPCResult{ - "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The txid(s) which the proof commits to, or empty array if the proof can not be validated."}, + } }, RPCExamples{""}, }.Check(request); @@ -390,7 +401,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request) " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, }, RPCResult{ - "\"transaction\" (string) hex string of the transaction\n" + RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction" }, RPCExamples{ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"") @@ -432,45 +443,53 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) }, }, RPCResult{ - "{\n" - " \"txid\" : \"id\", (string) The transaction id\n" - " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" - " \"size\" : n, (numeric) The transaction size\n" - " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" - " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n" - " \"version\" : n, (numeric) The version\n" - " \"locktime\" : ttt, (numeric) The lock time\n" - " \"vin\" : [ (array of json objects)\n" - " {\n" - " \"txid\" : \"id\", (string) The transaction id\n" - " \"vout\" : n, (numeric) The output number\n" - " \"scriptSig\" : { (json object) The script\n" - " \"asm\" : \"asm\", (string) asm\n" - " \"hex\" : \"hex\" (string) hex\n" - " },\n" - " \"txinwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " \"sequence\" : n (numeric) The script sequence number\n" - " }\n" - " ,...\n" - " ],\n" - " \"vout\" : [ (array of json objects)\n" - " {\n" - " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"n\" : n, (numeric) index\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) the asm\n" - " \"hex\" : \"hex\", (string) the hex\n" - " \"reqSigs\" : n, (numeric) The required sigs\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"addresses\" : [ (json array of string)\n" - " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) bitcoin address\n" - " ,...\n" - " ]\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"}, + {RPCResult::Type::NUM, "size", "The transaction size"}, + {RPCResult::Type::NUM, "vsize", "The virtual transaction size (differs from size for witness transactions)"}, + {RPCResult::Type::NUM, "weight", "The transaction's weight (between vsize*4 - 3 and vsize*4)"}, + {RPCResult::Type::NUM, "version", "The version"}, + {RPCResult::Type::NUM_TIME, "locktime", "The lock time"}, + {RPCResult::Type::ARR, "vin", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + {RPCResult::Type::NUM, "vout", "The output number"}, + {RPCResult::Type::OBJ, "scriptSig", "The script", + { + {RPCResult::Type::STR, "asm", "asm"}, + {RPCResult::Type::STR_HEX, "hex", "hex"}, + }}, + {RPCResult::Type::ARR, "txinwitness", "", + { + {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, + }}, + {RPCResult::Type::NUM, "sequence", "The script sequence number"}, + }}, + }}, + {RPCResult::Type::ARR, "vout", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::NUM, "n", "index"}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "the asm"}, + {RPCResult::Type::STR_HEX, "hex", "the hex"}, + {RPCResult::Type::NUM, "reqSigs", "The required sigs"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::ARR, "addresses", "", + { + {RPCResult::Type::STR, "address", "bitcoin address"}, + }}, + }}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("decoderawtransaction", "\"hexstring\"") @@ -513,26 +532,29 @@ static UniValue decodescript(const JSONRPCRequest& request) {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"}, }, RPCResult{ - "{\n" - " \"asm\" : \"asm\", (string) Script public key\n" - " \"type\" : \"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n" - " \"reqSigs\" : n, (numeric) The required signatures\n" - " \"addresses\" : [ (json array of string)\n" - " \"address\" (string) bitcoin address\n" - " ,...\n" - " ],\n" - " \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" - " \"segwit\" : { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n" - " \"asm\" : \"str\", (string) String representation of the script public key\n" - " \"hex\" : \"hexstr\", (string) Hex string of the script public key\n" - " \"type\" : \"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n" - " \"reqSigs\" : n, (numeric) The required signatures (always 1)\n" - " \"addresses\" : [ (json array of string) (always length 1)\n" - " \"address\" (string) segwit address\n" - " ,...\n" - " ],\n" - " \"p2sh-segwit\":\"str\" (string) address of the P2SH script wrapping this witness redeem script.\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "asm", "Script public key"}, + {RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"}, + {RPCResult::Type::NUM, "reqSigs", "The required signatures"}, + {RPCResult::Type::ARR, "addresses", "", + { + {RPCResult::Type::STR, "address", "bitcoin address"}, + }}, + {RPCResult::Type::STR, "p2sh", "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"}, + {RPCResult::Type::OBJ, "segwit", "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)", + { + {RPCResult::Type::STR, "asm", "String representation of the script public key"}, + {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"}, + {RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"}, + {RPCResult::Type::NUM, "reqSigs", "The required signatures (always 1)"}, + {RPCResult::Type::ARR, "addresses", "(always length 1)", + { + {RPCResult::Type::STR, "address", "segwit address"}, + }}, + {RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"}, + }}, + } }, RPCExamples{ HelpExampleCli("decodescript", "\"hexstring\"") @@ -607,7 +629,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) }, }, RPCResult{ - "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" + RPCResult::Type::STR, "", "The hex-encoded raw transaction with signature(s)" }, RPCExamples{ HelpExampleCli("combinerawtransaction", R"('["myhex1", "myhex2", "myhex3"]')") @@ -715,20 +737,22 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) }, }, 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" - " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" - " {\n" - " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" - " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" - " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" - " \"sequence\" : n, (numeric) Script sequence number\n" - " \"error\" : \"text\" (string) Verification or signing error related to the input\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"}, + {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"}, + {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"}, + {RPCResult::Type::NUM, "sequence", "Script sequence number"}, + {RPCResult::Type::STR, "error", "Verification or signing error related to the input"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"") @@ -784,7 +808,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) "/kB.\nSet to 0 to accept any fee rate.\n"}, }, RPCResult{ - "\"hex\" (string) The transaction hash in hex\n" + RPCResult::Type::STR_HEX, "", "The transaction hash in hex" }, RPCExamples{ "\nCreate a transaction\n" @@ -846,14 +870,16 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, }, RPCResult{ - "[ (json 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" - " \"txid\" (string) The transaction hash in hex\n" - " \"allowed\" (boolean) If the mempool allows this tx to be inserted\n" - " \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n" - " }\n" - "]\n" + RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n" + "Length is exactly one for now.", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, + {RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"}, + {RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"}, + }}, + } }, RPCExamples{ "\nCreate a transaction\n" @@ -950,92 +976,108 @@ UniValue decodepsbt(const JSONRPCRequest& request) {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"}, }, RPCResult{ - "{\n" - " \"tx\" : { (json object) The decoded network-serialized unsigned transaction.\n" - " ... The layout is the same as the output of decoderawtransaction.\n" - " },\n" - " \"unknown\" : { (json object) The unknown global fields\n" - " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" - " ...\n" - " },\n" - " \"inputs\" : [ (array of json objects)\n" - " {\n" - " \"non_witness_utxo\" : { (json object, optional) Decoded network transaction for non-witness UTXOs\n" - " ...\n" - " },\n" - " \"witness_utxo\" : { (json object, optional) Transaction output for witness UTXOs\n" - " \"amount\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" - " \"scriptPubKey\" : { (json object)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " \"address\" : \"address\" (string) Bitcoin address if there is one\n" - " }\n" - " },\n" - " \"partial_signatures\" : { (json object, optional)\n" - " \"pubkey\" : \"signature\", (string) The public key and signature that corresponds to it.\n" - " ,...\n" - " }\n" - " \"sighash\" : \"type\", (string, optional) The sighash type to be used\n" - " \"redeem_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"witness_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"bip32_derivs\" : { (json object, optional)\n" - " \"pubkey\" : { (json object, optional) The public key with the derivation path as the value.\n" - " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n" - " \"path\" : \"path\", (string) The path\n" - " }\n" - " ,...\n" - " }\n" - " \"final_scriptsig\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " }\n" - " \"final_scriptwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " \"unknown\" : { (json object) The unknown global fields\n" - " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" - " ...\n" - " },\n" - " }\n" - " ,...\n" - " ]\n" - " \"outputs\" : [ (array of json objects)\n" - " {\n" - " \"redeem_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"witness_script\" : { (json object, optional)\n" - " \"asm\" : \"asm\", (string) The asm\n" - " \"hex\" : \"hex\", (string) The hex\n" - " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" - " }\n" - " \"bip32_derivs\" : [ (array of json objects, optional)\n" - " {\n" - " \"pubkey\" : \"pubkey\", (string) The public key this path corresponds to\n" - " \"master_fingerprint\" : \"fingerprint\" (string) The fingerprint of the master key\n" - " \"path\" : \"path\", (string) The path\n" - " }\n" - " }\n" - " ,...\n" - " ],\n" - " \"unknown\" : { (json object) The unknown global fields\n" - " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" - " ...\n" - " },\n" - " }\n" - " ,...\n" - " ]\n" - " \"fee\" : fee (numeric, optional) The transaction fee paid if all UTXOs slots in the PSBT have been filled.\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "tx", "The decoded network-serialized unsigned transaction.", + { + {RPCResult::Type::ELISION, "", "The layout is the same as the output of decoderawtransaction."}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", + { + {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, + }}, + {RPCResult::Type::ARR, "inputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "non_witness_utxo", /* optional */ true, "Decoded network transaction for non-witness UTXOs", + { + {RPCResult::Type::ELISION, "",""}, + }}, + {RPCResult::Type::OBJ, "witness_utxo", /* optional */ true, "Transaction output for witness UTXOs", + { + {RPCResult::Type::NUM, "amount", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + {RPCResult::Type::STR, "address"," Bitcoin address if there is one"}, + }}, + }}, + {RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "", + { + {RPCResult::Type::STR, "pubkey", "The public key and signature that corresponds to it."}, + }}, + {RPCResult::Type::STR, "sighash", /* optional */ true, "The sighash type to be used"}, + {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "", + { + {RPCResult::Type::OBJ, "pubkey", /* optional */ true, "The public key with the derivation path as the value.", + { + {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + }}, + }}, + {RPCResult::Type::OBJ, "final_scriptsig", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR, "hex", "The hex"}, + }}, + {RPCResult::Type::ARR, "final_scriptwitness", "", + { + {RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", + { + {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, + }}, + }}, + }}, + {RPCResult::Type::ARR, "outputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR_HEX, "hex", "The hex"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "pubkey", "The public key this path corresponds to"}, + {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + }}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", + { + {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, + }}, + }}, + }}, + {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid if all UTXOs slots in the PSBT have been filled."}, + } }, RPCExamples{ HelpExampleCli("decodepsbt", "\"psbt\"") @@ -1244,7 +1286,7 @@ UniValue combinepsbt(const JSONRPCRequest& request) }, }, RPCResult{ - " \"psbt\" (string) The base64-encoded partially signed transaction\n" + RPCResult::Type::STR, "", "The base64-encoded partially signed transaction" }, RPCExamples{ HelpExampleCli("combinepsbt", R"('["mybase64_1", "mybase64_2", "mybase64_3"]')") @@ -1292,11 +1334,12 @@ UniValue finalizepsbt(const JSONRPCRequest& request) " extract and return the complete transaction in normal network serialization instead of the PSBT."}, }, RPCResult{ - "{ (json object)\n" - " \"psbt\" : \"str\", (string) The base64-encoded partially signed transaction if not extracted\n" - " \"hex\" : \"hex\", (string) The hex-encoded network transaction if extracted\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction if not extracted"}, + {RPCResult::Type::STR_HEX, "hex", "The hex-encoded network transaction if extracted"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + } }, RPCExamples{ HelpExampleCli("finalizepsbt", "\"psbt\"") @@ -1374,7 +1417,7 @@ UniValue createpsbt(const JSONRPCRequest& request) " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, }, RPCResult{ - " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" + RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)" }, RPCExamples{ HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") @@ -1431,7 +1474,7 @@ UniValue converttopsbt(const JSONRPCRequest& request) }, }, RPCResult{ - " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" + RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)" }, RPCExamples{ "\nCreate a transaction\n" @@ -1495,7 +1538,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request) }}, }, RPCResult { - " \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n" + RPCResult::Type::STR, "", "The base64-encoded partially signed transaction with inputs updated" }, RPCExamples { HelpExampleCli("utxoupdatepsbt", "\"psbt\"") @@ -1580,7 +1623,7 @@ UniValue joinpsbts(const JSONRPCRequest& request) }} }, RPCResult { - " \"psbt\" (string) The base64-encoded partially signed transaction\n" + RPCResult::Type::STR, "", "The base64-encoded partially signed transaction" }, RPCExamples { HelpExampleCli("joinpsbts", "\"psbt\"") @@ -1669,31 +1712,36 @@ UniValue analyzepsbt(const JSONRPCRequest& request) {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"} }, RPCResult { - "{\n" - " \"inputs\" : [ (array of json objects)\n" - " {\n" - " \"has_utxo\" : true|false (boolean) Whether a UTXO is provided\n" - " \"is_final\" : true|false (boolean) Whether the input is finalized\n" - " \"missing\" : { (json object, optional) Things that are missing that are required to complete this input\n" - " \"pubkeys\" : [ (array, optional)\n" - " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing\n" - " ]\n" - " \"signatures\" : [ (array, optional)\n" - " \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose signature is missing\n" - " ]\n" - " \"redeemscript\" : \"hash\" (string, optional) Hash160 of the redeemScript that is missing\n" - " \"witnessscript\" : \"hash\" (string, optional) SHA256 of the witnessScript that is missing\n" - " }\n" - " \"next\" : \"role\" (string, optional) Role of the next person that this input needs to go to\n" - " }\n" - " ,...\n" - " ]\n" - " \"estimated_vsize\" : vsize (numeric, optional) Estimated vsize of the final signed transaction\n" - " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n" - " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n" - " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n" - " \"error\" : \"error\" (string) Error message if there is one\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "inputs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "has_utxo", "Whether a UTXO is provided"}, + {RPCResult::Type::BOOL, "is_final", "Whether the input is finalized"}, + {RPCResult::Type::OBJ, "missing", /* optional */ true, "Things that are missing that are required to complete this input", + { + {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "", + { + {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing"}, + }}, + {RPCResult::Type::ARR, "signatures", /* optional */ true, "", + { + {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose signature is missing"}, + }}, + {RPCResult::Type::STR_HEX, "redeemscript", /* optional */ true, "Hash160 of the redeemScript that is missing"}, + {RPCResult::Type::STR_HEX, "witnessscript", /* optional */ true, "SHA256 of the witnessScript that is missing"}, + }}, + {RPCResult::Type::STR, "next", /* optional */ true, "Role of the next person that this input needs to go to"}, + }}, + }}, + {RPCResult::Type::NUM, "estimated_vsize", /* optional */ true, "Estimated vsize of the final signed transaction"}, + {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled"}, + {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"}, + {RPCResult::Type::STR, "next", "Role of the next person that this psbt needs to go to"}, + {RPCResult::Type::STR, "error", "Error message if there is one"}, + } }, RPCExamples { HelpExampleCli("analyzepsbt", "\"psbt\"") diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index b62490ed29..2893aa0e60 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -137,7 +137,7 @@ UniValue help(const JSONRPCRequest& jsonRequest) {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"}, }, RPCResult{ - "\"text\" (string) The help text\n" + RPCResult::Type::STR, "", "The help text" }, RPCExamples{""}, }.ToString() @@ -169,7 +169,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest) // this reply will get back to the client. StartShutdown(); if (jsonRequest.params[0].isNum()) { - MilliSleep(jsonRequest.params[0].get_int()); + UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()}); } return PACKAGE_NAME " stopping"; } @@ -180,7 +180,7 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest) "\nReturns the total uptime of the server.\n", {}, RPCResult{ - "ttt (numeric) The number of seconds that the server has been running\n" + RPCResult::Type::NUM, "", "The number of seconds that the server has been running" }, RPCExamples{ HelpExampleCli("uptime", "") @@ -197,16 +197,18 @@ static UniValue getrpcinfo(const JSONRPCRequest& request) "\nReturns details of the RPC server.\n", {}, RPCResult{ - "{\n" - " \"active_commands\" (json array) All active commands\n" - " [\n" - " { (json object) Information about an active command\n" - " \"method\" (string) The name of the RPC command \n" - " \"duration\" (numeric) The running time in microseconds\n" - " },...\n" - " ],\n" - " \"logpath\" : \"xxx\" (string) The complete file path to the debug log\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "active_commands", "All active commands", + { + {RPCResult::Type::OBJ, "", "Information about an active command", + { + {RPCResult::Type::STR, "method", "The name of the RPC command"}, + {RPCResult::Type::NUM, "duration", "The running time in microseconds"}, + }}, + }}, + {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"}, + } }, RPCExamples{ HelpExampleCli("getrpcinfo", "") diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 7cefb2db96..a4e67c8da8 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -312,20 +312,9 @@ struct Sections { } /** - * Serializing RPCArgs depends on the outer type. Only arrays and - * dictionaries can be nested in json. The top-level outer type is "named - * arguments", a mix between a dictionary and arrays. - */ - enum class OuterType { - ARR, - OBJ, - NAMED_ARG, // Only set on first recursion - }; - - /** * Recursive helper to translate an RPCArg into sections */ - void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG) + void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE) { const auto indent = std::string(current_indent, ' '); const auto indent_next = std::string(current_indent + 2, ' '); @@ -338,7 +327,7 @@ struct Sections { case RPCArg::Type::AMOUNT: case RPCArg::Type::RANGE: case RPCArg::Type::BOOL: { - if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion + if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion auto left = indent; if (arg.m_type_str.size() != 0 && push_name) { left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0); @@ -351,7 +340,7 @@ struct Sections { } case RPCArg::Type::OBJ: case RPCArg::Type::OBJ_USER_KEYS: { - const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(); + const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString(); PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right}); for (const auto& arg_inner : arg.m_inner) { Push(arg_inner, current_indent + 2, OuterType::OBJ); @@ -359,20 +348,20 @@ struct Sections { if (arg.m_type != RPCArg::Type::OBJ) { PushSection({indent_next + "...", ""}); } - PushSection({indent + "}" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""}); + PushSection({indent + "}" + (outer_type != OuterType::NONE ? "," : ""), ""}); break; } case RPCArg::Type::ARR: { auto left = indent; left += push_name ? "\"" + arg.m_name + "\": " : ""; left += "["; - const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString(); + const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString(); PushSection({left, right}); for (const auto& arg_inner : arg.m_inner) { Push(arg_inner, current_indent + 2, OuterType::ARR); } PushSection({indent_next + "...", ""}); - PushSection({indent + "]" + (outer_type != OuterType::NAMED_ARG ? "," : ""), ""}); + PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""}); break; } @@ -444,7 +433,9 @@ std::string RPCResults::ToDescriptionString() const } else { result += "\nResult (" + r.m_cond + "):\n"; } - result += r.m_result; + Sections sections; + r.ToSections(sections); + result += sections.ToString(); } return result; } @@ -591,6 +582,93 @@ std::string RPCArg::ToDescriptionString() const return ret; } +void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const +{ + // Indentation + const std::string indent(current_indent, ' '); + const std::string indent_next(current_indent + 2, ' '); + + // Elements in a JSON structure (dictionary or array) are separated by a comma + const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""}; + + // The key name if recursed into an dictionary + const std::string maybe_key{ + outer_type == OuterType::OBJ ? + "\"" + this->m_key_name + "\" : " : + ""}; + + // Format description with type + const auto Description = [&](const std::string& type) { + return "(" + type + (this->m_optional ? ", optional" : "") + ")" + + (this->m_description.empty() ? "" : " " + this->m_description); + }; + + switch (m_type) { + case Type::ELISION: { + // If the inner result is empty, use three dots for elision + sections.PushSection({indent_next + "...", m_description}); + return; + } + case Type::NONE: { + sections.PushSection({indent + "None", Description("json null")}); + return; + } + case Type::STR: { + sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")}); + return; + } + case Type::STR_AMOUNT: { + sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")}); + return; + } + case Type::STR_HEX: { + sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")}); + return; + } + case Type::NUM: { + sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")}); + return; + } + case Type::NUM_TIME: { + sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")}); + return; + } + case Type::BOOL: { + sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")}); + return; + } + case Type::ARR_FIXED: + case Type::ARR: { + sections.PushSection({indent + maybe_key + "[", Description("json array")}); + for (const auto& i : m_inner) { + i.ToSections(sections, OuterType::ARR, current_indent + 2); + } + if (m_type == Type::ARR) { + sections.PushSection({indent_next + "...", ""}); + } + sections.PushSection({indent + "]" + maybe_separator, ""}); + return; + } + case Type::OBJ_DYN: + case Type::OBJ: { + sections.PushSection({indent + maybe_key + "{", Description("json object")}); + for (const auto& i : m_inner) { + i.ToSections(sections, OuterType::OBJ, current_indent + 2); + } + if (m_type == Type::OBJ_DYN) { + // If the dictionary keys are dynamic, use three dots for continuation + sections.PushSection({indent_next + "...", ""}); + } + sections.PushSection({indent + "}" + maybe_separator, ""}); + return; + } + + // no default case, so the compiler can warn about missing cases + } + + CHECK_NONFATAL(false); +} + std::string RPCArg::ToStringObj(const bool oneline) const { std::string res; diff --git a/src/rpc/util.h b/src/rpc/util.h index f5edf69713..b5eebf0915 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -37,6 +37,7 @@ extern const std::string EXAMPLE_ADDRESS; class FillableSigningProvider; class CPubKey; class CScript; +struct Sections; /** Wrapper for UniValue::VType, which includes typeAny: * Used to denote don't care type. */ @@ -101,6 +102,16 @@ std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, Fl /** Returns, given services flags, a list of humanly readable (known) network services */ UniValue GetServicesNames(ServiceFlags services); +/** + * Serializing JSON objects depends on the outer type. Only arrays and + * dictionaries can be nested in json. The top-level outer type is "NONE". + */ +enum class OuterType { + ARR, + OBJ, + NONE, // Only set on first recursion +}; + struct RPCArg { enum class Type { OBJ, @@ -140,37 +151,37 @@ struct RPCArg { const std::vector<std::string> m_type_str; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_type_str.at(0) will override the type of the value in a key-value pair, m_type_str.at(1) will override the type in the argument description. RPCArg( - const std::string& name, - const Type& type, - const Fallback& fallback, - const std::string& description, - const std::string& oneline_description = "", - const std::vector<std::string>& type_str = {}) - : m_name{name}, - m_type{type}, - m_fallback{fallback}, - m_description{description}, - m_oneline_description{oneline_description}, - m_type_str{type_str} + const std::string name, + const Type type, + const Fallback fallback, + const std::string description, + const std::string oneline_description = "", + const std::vector<std::string> type_str = {}) + : m_name{std::move(name)}, + m_type{std::move(type)}, + m_fallback{std::move(fallback)}, + m_description{std::move(description)}, + m_oneline_description{std::move(oneline_description)}, + m_type_str{std::move(type_str)} { CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ); } RPCArg( - const std::string& name, - const Type& type, - const Fallback& fallback, - const std::string& description, - const std::vector<RPCArg>& inner, - const std::string& oneline_description = "", - const std::vector<std::string>& type_str = {}) - : m_name{name}, - m_type{type}, - m_inner{inner}, - m_fallback{fallback}, - m_description{description}, - m_oneline_description{oneline_description}, - m_type_str{type_str} + const std::string name, + const Type type, + const Fallback fallback, + const std::string description, + const std::vector<RPCArg> inner, + const std::string oneline_description = "", + const std::vector<std::string> type_str = {}) + : m_name{std::move(name)}, + m_type{std::move(type)}, + m_inner{std::move(inner)}, + m_fallback{std::move(fallback)}, + m_description{std::move(description)}, + m_oneline_description{std::move(oneline_description)}, + m_type_str{std::move(type_str)} { CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ); } @@ -195,21 +206,85 @@ struct RPCArg { }; struct RPCResult { + enum class Type { + OBJ, + ARR, + STR, + NUM, + BOOL, + NONE, + STR_AMOUNT, //!< Special string to represent a floating point amount + STR_HEX, //!< Special string with only hex chars + OBJ_DYN, //!< Special dictionary with keys that are not literals + ARR_FIXED, //!< Special array that has a fixed number of entries + NUM_TIME, //!< Special numeric to denote unix epoch time + ELISION, //!< Special type to denote elision (...) + }; + + const Type m_type; + const std::string m_key_name; //!< Only used for dicts + const std::vector<RPCResult> m_inner; //!< Only used for arrays or dicts + const bool m_optional; + const std::string m_description; const std::string m_cond; - const std::string m_result; - explicit RPCResult(std::string result) - : m_cond{}, m_result{std::move(result)} + RPCResult( + const std::string cond, + const Type type, + const std::string m_key_name, + const bool optional, + const std::string description, + const std::vector<RPCResult> inner = {}) + : m_type{std::move(type)}, + m_key_name{std::move(m_key_name)}, + m_inner{std::move(inner)}, + m_optional{optional}, + m_description{std::move(description)}, + m_cond{std::move(cond)} { - CHECK_NONFATAL(!m_result.empty()); + CHECK_NONFATAL(!m_cond.empty()); + const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN}; + CHECK_NONFATAL(inner_needed != inner.empty()); } - RPCResult(std::string cond, std::string result) - : m_cond{std::move(cond)}, m_result{std::move(result)} + RPCResult( + const std::string cond, + const Type type, + const std::string m_key_name, + const std::string description, + const std::vector<RPCResult> inner = {}) + : RPCResult{cond, type, m_key_name, false, description, inner} {} + + RPCResult( + const Type type, + const std::string m_key_name, + const bool optional, + const std::string description, + const std::vector<RPCResult> inner = {}) + : m_type{std::move(type)}, + m_key_name{std::move(m_key_name)}, + m_inner{std::move(inner)}, + m_optional{optional}, + m_description{std::move(description)}, + m_cond{} { - CHECK_NONFATAL(!m_cond.empty()); - CHECK_NONFATAL(!m_result.empty()); + const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN}; + CHECK_NONFATAL(inner_needed != inner.empty()); } + + RPCResult( + const Type type, + const std::string m_key_name, + const std::string description, + const std::vector<RPCResult> inner = {}) + : RPCResult{type, m_key_name, false, description, inner} {} + + /** Append the sections of the result. */ + void ToSections(Sections& sections, OuterType outer_type = OuterType::NONE, const int current_indent = 0) const; + /** Return the type string of the result when it is in an object (dict). */ + std::string ToStringObj() const; + /** Return the description string, including the result type. */ + std::string ToDescriptionString() const; }; struct RPCResults { diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 72cca89d99..7cb7754fde 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -5,7 +5,6 @@ #include <scheduler.h> #include <random.h> -#include <reverselock.h> #include <assert.h> #include <utility> @@ -20,18 +19,9 @@ CScheduler::~CScheduler() } -#if BOOST_VERSION < 105000 -static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t) -{ - // Creating the posix_time using from_time_t loses sub-second precision. So rather than exporting the time_point to time_t, - // start with a posix_time at the epoch (0) and add the milliseconds that have passed since then. - return boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(boost::chrono::duration_cast<boost::chrono::milliseconds>(t.time_since_epoch()).count()); -} -#endif - void CScheduler::serviceQueue() { - boost::unique_lock<boost::mutex> lock(newTaskMutex); + WAIT_LOCK(newTaskMutex, lock); ++nThreadsServicingQueue; // newTaskMutex is locked throughout this loop EXCEPT @@ -40,7 +30,7 @@ void CScheduler::serviceQueue() while (!shouldStop()) { try { if (!shouldStop() && taskQueue.empty()) { - reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + REVERSE_LOCK(lock); } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. @@ -50,21 +40,13 @@ void CScheduler::serviceQueue() // Wait until either there is a new task, or until // the time of the first item on the queue: -// wait_until needs boost 1.50 or later; older versions have timed_wait: -#if BOOST_VERSION < 105000 - while (!shouldStop() && !taskQueue.empty() && - newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) { - // Keep waiting until timeout - } -#else - // Some boost versions have a conflicting overload of wait_until that returns void. - // Explicitly use a template here to avoid hitting that overload. while (!shouldStop() && !taskQueue.empty()) { - boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first; - if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout) + std::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first; + if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) { break; // Exit loop after timeout, it means we reached the time of the event + } } -#endif + // If there are multiple threads, the queue can empty while we're waiting (another // thread may service the task we were waiting on). if (shouldStop() || taskQueue.empty()) @@ -76,7 +58,7 @@ void CScheduler::serviceQueue() { // Unlock before calling f, so it can reschedule itself or another task // without deadlocking: - reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + REVERSE_LOCK(lock); f(); } } catch (...) { @@ -91,7 +73,7 @@ void CScheduler::serviceQueue() void CScheduler::stop(bool drain) { { - boost::unique_lock<boost::mutex> lock(newTaskMutex); + LOCK(newTaskMutex); if (drain) stopWhenEmpty = true; else @@ -100,10 +82,10 @@ void CScheduler::stop(bool drain) newTaskScheduled.notify_all(); } -void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) +void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::time_point t) { { - boost::unique_lock<boost::mutex> lock(newTaskMutex); + LOCK(newTaskMutex); taskQueue.insert(std::make_pair(t, f)); } newTaskScheduled.notify_one(); @@ -111,18 +93,18 @@ void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::t void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds) { - schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds)); + schedule(f, std::chrono::system_clock::now() + std::chrono::milliseconds(deltaMilliSeconds)); } -void CScheduler::MockForward(boost::chrono::seconds delta_seconds) +void CScheduler::MockForward(std::chrono::seconds delta_seconds) { - assert(delta_seconds.count() > 0 && delta_seconds < boost::chrono::hours{1}); + assert(delta_seconds.count() > 0 && delta_seconds < std::chrono::hours{1}); { - boost::unique_lock<boost::mutex> lock(newTaskMutex); + LOCK(newTaskMutex); // use temp_queue to maintain updated schedule - std::multimap<boost::chrono::system_clock::time_point, Function> temp_queue; + std::multimap<std::chrono::system_clock::time_point, Function> temp_queue; for (const auto& element : taskQueue) { temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second); @@ -147,10 +129,10 @@ void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds); } -size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, - boost::chrono::system_clock::time_point &last) const +size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point &first, + std::chrono::system_clock::time_point &last) const { - boost::unique_lock<boost::mutex> lock(newTaskMutex); + LOCK(newTaskMutex); size_t result = taskQueue.size(); if (!taskQueue.empty()) { first = taskQueue.begin()->first; @@ -160,7 +142,7 @@ size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, } bool CScheduler::AreThreadsServicingQueue() const { - boost::unique_lock<boost::mutex> lock(newTaskMutex); + LOCK(newTaskMutex); return nThreadsServicingQueue; } @@ -174,7 +156,7 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() { if (m_are_callbacks_running) return; if (m_callbacks_pending.empty()) return; } - m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this)); + m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this), std::chrono::system_clock::now()); } void SingleThreadedSchedulerClient::ProcessQueue() { diff --git a/src/scheduler.h b/src/scheduler.h index d18be0ea5e..4d5aa3068e 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -7,11 +7,12 @@ // // NOTE: -// boost::thread / boost::chrono should be ported to std::thread / std::chrono +// boost::thread should be ported to std::thread // when we support C++11. // -#include <boost/chrono/chrono.hpp> -#include <boost/thread.hpp> +#include <condition_variable> +#include <functional> +#include <list> #include <map> #include <sync.h> @@ -27,8 +28,8 @@ // s->scheduleFromNow(std::bind(Class::func, this, argument), 3); // boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s)); // -// ... then at program shutdown, clean up the thread running serviceQueue: -// t->interrupt(); +// ... then at program shutdown, make sure to call stop() to clean up the thread(s) running serviceQueue: +// s->stop(); // t->join(); // delete t; // delete s; // Must be done after thread is interrupted/joined. @@ -43,7 +44,7 @@ public: typedef std::function<void()> Function; // Call func at/after time t - void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now()); + void schedule(Function f, std::chrono::system_clock::time_point t); // Convenience method: call f once deltaMilliSeconds from now void scheduleFromNow(Function f, int64_t deltaMilliSeconds); @@ -60,7 +61,7 @@ public: * Iterates through items on taskQueue and reschedules them * to be delta_seconds sooner. */ - void MockForward(boost::chrono::seconds delta_seconds); + void MockForward(std::chrono::seconds delta_seconds); // To keep things as simple as possible, there is no unschedule. @@ -75,20 +76,20 @@ public: // Returns number of tasks waiting to be serviced, // and first and last task times - size_t getQueueInfo(boost::chrono::system_clock::time_point &first, - boost::chrono::system_clock::time_point &last) const; + size_t getQueueInfo(std::chrono::system_clock::time_point &first, + std::chrono::system_clock::time_point &last) const; // Returns true if there are threads actively running in serviceQueue() bool AreThreadsServicingQueue() const; private: - std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue; - boost::condition_variable newTaskScheduled; - mutable boost::mutex newTaskMutex; - int nThreadsServicingQueue; - bool stopRequested; - bool stopWhenEmpty; - bool shouldStop() const { return stopRequested || (stopWhenEmpty && taskQueue.empty()); } + mutable Mutex newTaskMutex; + std::condition_variable newTaskScheduled; + std::multimap<std::chrono::system_clock::time_point, Function> taskQueue GUARDED_BY(newTaskMutex); + int nThreadsServicingQueue GUARDED_BY(newTaskMutex); + bool stopRequested GUARDED_BY(newTaskMutex); + bool stopWhenEmpty GUARDED_BY(newTaskMutex); + bool shouldStop() const EXCLUSIVE_LOCKS_REQUIRED(newTaskMutex) { return stopRequested || (stopWhenEmpty && taskQueue.empty()); } }; /** diff --git a/src/serialize.h b/src/serialize.h index 0a43913f83..5045cb3c7f 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -500,7 +500,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T& #define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj) #define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj) -#define COMPACTSIZE(obj) CCompactSize(REF(obj)) +#define COMPACTSIZE(obj) Using<CompactSizeFormatter>(obj) #define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj)) /** Serialization wrapper class for integers in VarInt format. */ @@ -518,6 +518,28 @@ struct VarIntFormatter } }; +template<int Bytes> +struct CustomUintFormatter +{ + static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range"); + static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes)); + + template <typename Stream, typename I> void Ser(Stream& s, I v) + { + if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range"); + uint64_t raw = htole64(v); + s.write((const char*)&raw, Bytes); + } + + template <typename Stream, typename I> void Unser(Stream& s, I& v) + { + static_assert(std::numeric_limits<I>::max() >= MAX && std::numeric_limits<I>::min() <= 0, "CustomUintFormatter type too small"); + uint64_t raw = 0; + s.read((char*)&raw, Bytes); + v = le64toh(raw); + } +}; + /** Serialization wrapper class for big-endian integers. * * Use this wrapper around integer types that are stored in memory in native @@ -552,21 +574,26 @@ public: } }; -class CCompactSize +/** Formatter for integers in CompactSize format. */ +struct CompactSizeFormatter { -protected: - uint64_t &n; -public: - explicit CCompactSize(uint64_t& nIn) : n(nIn) { } - - template<typename Stream> - void Serialize(Stream &s) const { - WriteCompactSize<Stream>(s, n); + template<typename Stream, typename I> + void Unser(Stream& s, I& v) + { + uint64_t n = ReadCompactSize<Stream>(s); + if (n < std::numeric_limits<I>::min() || n > std::numeric_limits<I>::max()) { + throw std::ios_base::failure("CompactSize exceeds limit of type"); + } + v = n; } - template<typename Stream> - void Unserialize(Stream& s) { - n = ReadCompactSize<Stream>(s); + template<typename Stream, typename I> + void Ser(Stream& s, I v) + { + static_assert(std::is_unsigned<I>::value, "CompactSize only supported for unsigned integers"); + static_assert(std::numeric_limits<I>::max() <= std::numeric_limits<uint64_t>::max(), "CompactSize only supports 64-bit integers and below"); + + WriteCompactSize<Stream>(s, v); } }; @@ -613,7 +640,7 @@ BigEndian<I> WrapBigEndian(I& n) { return BigEndian<I>(n); } * as a vector of VarInt-encoded integers. * * V is not required to be an std::vector type. It works for any class that - * exposes a value_type, size, reserve, push_back, and const iterators. + * exposes a value_type, size, reserve, emplace_back, back, and const iterators. */ template<class Formatter> struct VectorFormatter @@ -621,15 +648,17 @@ struct VectorFormatter template<typename Stream, typename V> void Ser(Stream& s, const V& v) { + Formatter formatter; WriteCompactSize(s, v.size()); for (const typename V::value_type& elem : v) { - s << Using<Formatter>(elem); + formatter.Ser(s, elem); } } template<typename Stream, typename V> void Unser(Stream& s, V& v) { + Formatter formatter; v.clear(); size_t size = ReadCompactSize(s); size_t allocated = 0; @@ -641,9 +670,8 @@ struct VectorFormatter allocated = std::min(size, allocated + MAX_VECTOR_ALLOCATE / sizeof(typename V::value_type)); v.reserve(allocated); while (v.size() < allocated) { - typename V::value_type val; - s >> Using<Formatter>(val); - v.push_back(std::move(val)); + v.emplace_back(); + formatter.Unser(s, v.back()); } } }; diff --git a/src/sync.cpp b/src/sync.cpp index 924e7b5bb0..71657a7439 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -13,7 +13,7 @@ #include <util/strencodings.h> #include <util/threadnames.h> - +#include <system_error> #include <map> #include <set> @@ -60,6 +60,11 @@ struct CLockLocation { mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name); } + std::string Name() const + { + return mutexName; + } + private: bool fTry; std::string mutexName; @@ -155,6 +160,18 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName())); } +void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) +{ + if (!g_lockstack.empty()) { + const auto& lastlock = g_lockstack.back(); + if (lastlock.first == cs) { + lockname = lastlock.second.Name(); + return; + } + } + throw std::system_error(EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname)); +} + void LeaveCritical() { pop_lock(); diff --git a/src/sync.h b/src/sync.h index 0cdbb59c70..204734c273 100644 --- a/src/sync.h +++ b/src/sync.h @@ -50,6 +50,7 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); void LeaveCritical(); +void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line); std::string LocksHeld(); void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs); void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); @@ -64,6 +65,7 @@ extern bool g_debug_lockorder_abort; #else void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} +void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {} void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {} void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} void static inline DeleteLock(void* cs) {} @@ -171,8 +173,45 @@ public: { return Base::owns_lock(); } + +protected: + // needed for reverse_lock + UniqueLock() { } + +public: + /** + * An RAII-style reverse lock. Unlocks on construction and locks on destruction. + */ + class reverse_lock { + public: + explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) { + CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line); + lock.unlock(); + LeaveCritical(); + lock.swap(templock); + } + + ~reverse_lock() { + templock.swap(lock); + EnterCritical(lockname.c_str(), file.c_str(), line, (void*)lock.mutex()); + lock.lock(); + } + + private: + reverse_lock(reverse_lock const&); + reverse_lock& operator=(reverse_lock const&); + + UniqueLock& lock; + UniqueLock templock; + std::string lockname; + const std::string file; + const int line; + }; + friend class reverse_lock; }; +#define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) + template<typename MutexArg> using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>; diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index 79e18cd2c0..5e52dc268f 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) int64_t time_start = GetTimeMillis(); while (!filter_index.BlockUntilSyncedToCurrentChain()) { BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis()); - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } // Check that filter index has all blocks that were in the chain before it started. diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 482fe3772c..a9628e85f9 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -393,7 +393,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) CCheckQueueControl<FakeCheck> control(queue.get()); // While sleeping, no other thread should execute to this point auto observed = ++nThreads; - MilliSleep(10); + UninterruptibleSleep(std::chrono::milliseconds{10}); fails += observed != nThreads; }); } diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp new file mode 100644 index 0000000000..a24bae5b35 --- /dev/null +++ b/src/test/fuzz/float.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <memusage.h> +#include <serialize.h> +#include <streams.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <version.h> + +#include <cassert> +#include <cstdint> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + + { + const double d = fuzzed_data_provider.ConsumeFloatingPoint<double>(); + (void)memusage::DynamicUsage(d); + assert(ser_uint64_to_double(ser_double_to_uint64(d)) == d); + + CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); + stream << d; + double d_deserialized; + stream >> d_deserialized; + assert(d == d_deserialized); + } + + { + const float f = fuzzed_data_provider.ConsumeFloatingPoint<float>(); + (void)memusage::DynamicUsage(f); + assert(ser_uint32_to_float(ser_float_to_uint32(f)) == f); + + CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); + stream << f; + float f_deserialized; + stream >> f_deserialized; + assert(f == f_deserialized); + } +} diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index 723938bcdb..2d47c631cb 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <amount.h> #include <arith_uint256.h> #include <compressor.h> #include <consensus/merkle.h> @@ -18,12 +19,14 @@ #include <script/signingprovider.h> #include <script/standard.h> #include <serialize.h> +#include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <uint256.h> #include <util/strencodings.h> #include <util/system.h> #include <util/time.h> +#include <version.h> #include <cassert> #include <limits> @@ -53,10 +56,18 @@ void test_one_input(const std::vector<uint8_t>& buffer) // We cannot assume a specific value of std::is_signed<char>::value: // ConsumeIntegral<char>() instead of casting from {u,}int8_t. const char ch = fuzzed_data_provider.ConsumeIntegral<char>(); + const bool b = fuzzed_data_provider.ConsumeBool(); const Consensus::Params& consensus_params = Params().GetConsensus(); (void)CheckProofOfWork(u256, u32, consensus_params); - (void)CompressAmount(u64); + if (u64 <= MAX_MONEY) { + const uint64_t compressed_money_amount = CompressAmount(u64); + assert(u64 == DecompressAmount(compressed_money_amount)); + static const uint64_t compressed_money_amount_max = CompressAmount(MAX_MONEY - 1); + assert(compressed_money_amount <= compressed_money_amount_max); + } else { + (void)CompressAmount(u64); + } static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000")); static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); const std::vector<uint256> v256{u256, u256_min, u256_max}; @@ -124,4 +135,68 @@ void test_one_input(const std::vector<uint8_t>& buffer) (void)GetScriptForDestination(destination); (void)IsValidDestination(destination); } + + { + CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); + + uint256 deserialized_u256; + stream << u256; + stream >> deserialized_u256; + assert(u256 == deserialized_u256 && stream.empty()); + + uint160 deserialized_u160; + stream << u160; + stream >> deserialized_u160; + assert(u160 == deserialized_u160 && stream.empty()); + + uint64_t deserialized_u64; + stream << u64; + stream >> deserialized_u64; + assert(u64 == deserialized_u64 && stream.empty()); + + int64_t deserialized_i64; + stream << i64; + stream >> deserialized_i64; + assert(i64 == deserialized_i64 && stream.empty()); + + uint32_t deserialized_u32; + stream << u32; + stream >> deserialized_u32; + assert(u32 == deserialized_u32 && stream.empty()); + + int32_t deserialized_i32; + stream << i32; + stream >> deserialized_i32; + assert(i32 == deserialized_i32 && stream.empty()); + + uint16_t deserialized_u16; + stream << u16; + stream >> deserialized_u16; + assert(u16 == deserialized_u16 && stream.empty()); + + int16_t deserialized_i16; + stream << i16; + stream >> deserialized_i16; + assert(i16 == deserialized_i16 && stream.empty()); + + uint8_t deserialized_u8; + stream << u8; + stream >> deserialized_u8; + assert(u8 == deserialized_u8 && stream.empty()); + + int8_t deserialized_i8; + stream << i8; + stream >> deserialized_i8; + assert(i8 == deserialized_i8 && stream.empty()); + + char deserialized_ch; + stream << ch; + stream >> deserialized_ch; + assert(ch == deserialized_ch && stream.empty()); + + bool deserialized_b; + stream << b; + stream >> deserialized_b; + assert(b == deserialized_b && stream.empty()); + } } diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp new file mode 100644 index 0000000000..1919a5f881 --- /dev/null +++ b/src/test/fuzz/key.cpp @@ -0,0 +1,309 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <chainparams.h> +#include <chainparamsbase.h> +#include <key.h> +#include <key_io.h> +#include <outputtype.h> +#include <policy/policy.h> +#include <pubkey.h> +#include <rpc/util.h> +#include <script/keyorigin.h> +#include <script/script.h> +#include <script/sign.h> +#include <script/signingprovider.h> +#include <script/standard.h> +#include <streams.h> +#include <test/fuzz/fuzz.h> +#include <util/memory.h> +#include <util/strencodings.h> + +#include <cassert> +#include <cstdint> +#include <numeric> +#include <string> +#include <vector> + +void initialize() +{ + static const ECCVerifyHandle ecc_verify_handle; + ECC_Start(); + SelectParams(CBaseChainParams::REGTEST); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const CKey key = [&] { + CKey k; + k.Set(buffer.begin(), buffer.end(), true); + return k; + }(); + if (!key.IsValid()) { + return; + } + + { + assert(key.begin() + key.size() == key.end()); + assert(key.IsCompressed()); + assert(key.size() == 32); + assert(DecodeSecret(EncodeSecret(key)) == key); + } + + { + CKey invalid_key; + assert(!(invalid_key == key)); + assert(!invalid_key.IsCompressed()); + assert(!invalid_key.IsValid()); + assert(invalid_key.size() == 0); + } + + { + CKey uncompressed_key; + uncompressed_key.Set(buffer.begin(), buffer.end(), false); + assert(!(uncompressed_key == key)); + assert(!uncompressed_key.IsCompressed()); + assert(key.size() == 32); + assert(uncompressed_key.begin() + uncompressed_key.size() == uncompressed_key.end()); + assert(uncompressed_key.IsValid()); + } + + { + CKey copied_key; + copied_key.Set(key.begin(), key.end(), key.IsCompressed()); + assert(copied_key == key); + } + + { + CKey negated_key = key; + negated_key.Negate(); + assert(negated_key.IsValid()); + assert(!(negated_key == key)); + + negated_key.Negate(); + assert(negated_key == key); + } + + const uint256 random_uint256 = Hash(buffer.begin(), buffer.end()); + + { + CKey child_key; + ChainCode child_chaincode; + const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256); + assert(ok); + assert(child_key.IsValid()); + assert(!(child_key == key)); + assert(child_chaincode != random_uint256); + } + + const CPubKey pubkey = key.GetPubKey(); + + { + assert(pubkey.size() == 33); + assert(key.VerifyPubKey(pubkey)); + assert(pubkey.GetHash() != random_uint256); + assert(pubkey.begin() + pubkey.size() == pubkey.end()); + assert(pubkey.data() == pubkey.begin()); + assert(pubkey.IsCompressed()); + assert(pubkey.IsValid()); + assert(pubkey.IsFullyValid()); + assert(HexToPubKey(HexStr(pubkey.begin(), pubkey.end())) == pubkey); + assert(GetAllDestinationsForKey(pubkey).size() == 3); + } + + { + CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION}; + pubkey.Serialize(data_stream); + + CPubKey pubkey_deserialized; + pubkey_deserialized.Unserialize(data_stream); + assert(pubkey_deserialized == pubkey); + } + + { + const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey); + assert(!tx_pubkey_script.IsPayToScriptHash()); + assert(!tx_pubkey_script.IsPayToWitnessScriptHash()); + assert(!tx_pubkey_script.IsPushOnly()); + assert(!tx_pubkey_script.IsUnspendable()); + assert(tx_pubkey_script.HasValidOps()); + assert(tx_pubkey_script.size() == 35); + + const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey}); + assert(!tx_multisig_script.IsPayToScriptHash()); + assert(!tx_multisig_script.IsPayToWitnessScriptHash()); + assert(!tx_multisig_script.IsPushOnly()); + assert(!tx_multisig_script.IsUnspendable()); + assert(tx_multisig_script.HasValidOps()); + assert(tx_multisig_script.size() == 37); + + FillableSigningProvider fillable_signing_provider; + assert(IsSolvable(fillable_signing_provider, tx_pubkey_script)); + assert(IsSolvable(fillable_signing_provider, tx_multisig_script)); + assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script)); + assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script)); + assert(fillable_signing_provider.GetKeys().size() == 0); + assert(!fillable_signing_provider.HaveKey(pubkey.GetID())); + + const bool ok_add_key = fillable_signing_provider.AddKey(key); + assert(ok_add_key); + assert(fillable_signing_provider.HaveKey(pubkey.GetID())); + + FillableSigningProvider fillable_signing_provider_pub; + assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID())); + + const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey); + assert(ok_add_key_pubkey); + assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID())); + + txnouttype which_type_tx_pubkey; + const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, which_type_tx_pubkey); + assert(is_standard_tx_pubkey); + assert(which_type_tx_pubkey == txnouttype::TX_PUBKEY); + + txnouttype which_type_tx_multisig; + const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, which_type_tx_multisig); + assert(is_standard_tx_multisig); + assert(which_type_tx_multisig == txnouttype::TX_MULTISIG); + + std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey; + const txnouttype outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey); + assert(outtype_tx_pubkey == txnouttype::TX_PUBKEY); + assert(v_solutions_ret_tx_pubkey.size() == 1); + assert(v_solutions_ret_tx_pubkey[0].size() == 33); + + std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig; + const txnouttype outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig); + assert(outtype_tx_multisig == txnouttype::TX_MULTISIG); + assert(v_solutions_ret_tx_multisig.size() == 3); + assert(v_solutions_ret_tx_multisig[0].size() == 1); + assert(v_solutions_ret_tx_multisig[1].size() == 33); + assert(v_solutions_ret_tx_multisig[2].size() == 1); + + OutputType output_type{}; + const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type); + assert(output_type == OutputType::LEGACY); + assert(IsValidDestination(tx_destination)); + assert(CTxDestination{PKHash{pubkey}} == tx_destination); + + const CScript script_for_destination = GetScriptForDestination(tx_destination); + assert(script_for_destination.size() == 25); + + const std::string destination_address = EncodeDestination(tx_destination); + assert(DecodeDestination(destination_address) == tx_destination); + + const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address); + assert(pubkey_from_address_string == pubkey); + + CKeyID key_id = pubkey.GetID(); + assert(!key_id.IsNull()); + assert(key_id == CKeyID{key_id}); + assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination)); + + CPubKey pubkey_out; + const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out); + assert(ok_get_pubkey); + + CKey key_out; + const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out); + assert(ok_get_key); + assert(fillable_signing_provider.GetKeys().size() == 1); + assert(fillable_signing_provider.HaveKey(key_id)); + + KeyOriginInfo key_origin_info; + const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info); + assert(!ok_get_key_origin); + } + + { + const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()}; + assert(CPubKey::ValidSize(vch_pubkey)); + assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1})); + + const CPubKey pubkey_ctor_1{vch_pubkey}; + assert(pubkey == pubkey_ctor_1); + + const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()}; + assert(pubkey == pubkey_ctor_2); + + CPubKey pubkey_set; + pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end()); + assert(pubkey == pubkey_set); + } + + { + const CPubKey invalid_pubkey{}; + assert(!invalid_pubkey.IsValid()); + assert(!invalid_pubkey.IsFullyValid()); + assert(!(pubkey == invalid_pubkey)); + assert(pubkey != invalid_pubkey); + assert(pubkey < invalid_pubkey); + } + + { + // Cover CPubKey's operator[](unsigned int pos) + unsigned int sum = 0; + for (size_t i = 0; i < pubkey.size(); ++i) { + sum += pubkey[i]; + } + assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum); + } + + { + CPubKey decompressed_pubkey = pubkey; + assert(decompressed_pubkey.IsCompressed()); + + const bool ok = decompressed_pubkey.Decompress(); + assert(ok); + assert(!decompressed_pubkey.IsCompressed()); + assert(decompressed_pubkey.size() == 65); + } + + { + std::vector<unsigned char> vch_sig; + const bool ok = key.Sign(random_uint256, vch_sig, false); + assert(ok); + assert(pubkey.Verify(random_uint256, vch_sig)); + assert(CPubKey::CheckLowS(vch_sig)); + + const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1}; + assert(!pubkey.Verify(random_uint256, vch_invalid_sig)); + assert(!CPubKey::CheckLowS(vch_invalid_sig)); + } + + { + std::vector<unsigned char> vch_compact_sig; + const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig); + assert(ok_sign_compact); + + CPubKey recover_pubkey; + const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig); + assert(ok_recover_compact); + assert(recover_pubkey == pubkey); + } + + { + CPubKey child_pubkey; + ChainCode child_chaincode; + const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256); + assert(ok); + assert(child_pubkey != pubkey); + assert(child_pubkey.IsCompressed()); + assert(child_pubkey.IsFullyValid()); + assert(child_pubkey.IsValid()); + assert(child_pubkey.size() == 33); + assert(child_chaincode != random_uint256); + } + + const CPrivKey priv_key = key.GetPrivKey(); + + { + for (const bool skip_check : {true, false}) { + CKey loaded_key; + const bool ok = loaded_key.Load(priv_key, pubkey, skip_check); + assert(ok); + assert(key == loaded_key); + } + } +} diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp new file mode 100644 index 0000000000..c8288123e8 --- /dev/null +++ b/src/test/fuzz/locale.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/fuzz/fuzz.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <tinyformat.h> +#include <util/strencodings.h> + +#include <cassert> +#include <clocale> +#include <cstdint> +#include <locale> +#include <string> +#include <vector> + +namespace { +const std::string locale_identifiers[] = { + "C", "C.UTF-8", "aa_DJ", "aa_DJ.ISO-8859-1", "aa_DJ.UTF-8", "aa_ER", "aa_ER.UTF-8", "aa_ET", "aa_ET.UTF-8", "af_ZA", "af_ZA.ISO-8859-1", "af_ZA.UTF-8", "agr_PE", "agr_PE.UTF-8", "ak_GH", "ak_GH.UTF-8", "am_ET", "am_ET.UTF-8", "an_ES", "an_ES.ISO-8859-15", "an_ES.UTF-8", "anp_IN", "anp_IN.UTF-8", "ar_AE", "ar_AE.ISO-8859-6", "ar_AE.UTF-8", "ar_BH", "ar_BH.ISO-8859-6", "ar_BH.UTF-8", "ar_DZ", "ar_DZ.ISO-8859-6", "ar_DZ.UTF-8", "ar_EG", "ar_EG.ISO-8859-6", "ar_EG.UTF-8", "ar_IN", "ar_IN.UTF-8", "ar_IQ", "ar_IQ.ISO-8859-6", "ar_IQ.UTF-8", "ar_JO", "ar_JO.ISO-8859-6", "ar_JO.UTF-8", "ar_KW", "ar_KW.ISO-8859-6", "ar_KW.UTF-8", "ar_LB", "ar_LB.ISO-8859-6", "ar_LB.UTF-8", "ar_LY", "ar_LY.ISO-8859-6", "ar_LY.UTF-8", "ar_MA", "ar_MA.ISO-8859-6", "ar_MA.UTF-8", "ar_OM", "ar_OM.ISO-8859-6", "ar_OM.UTF-8", "ar_QA", "ar_QA.ISO-8859-6", "ar_QA.UTF-8", "ar_SA", "ar_SA.ISO-8859-6", "ar_SA.UTF-8", "ar_SD", "ar_SD.ISO-8859-6", "ar_SD.UTF-8", "ar_SS", "ar_SS.UTF-8", "ar_SY", "ar_SY.ISO-8859-6", "ar_SY.UTF-8", "ar_TN", "ar_TN.ISO-8859-6", "ar_TN.UTF-8", "ar_YE", "ar_YE.ISO-8859-6", "ar_YE.UTF-8", "as_IN", "as_IN.UTF-8", "ast_ES", "ast_ES.ISO-8859-15", "ast_ES.UTF-8", "ayc_PE", "ayc_PE.UTF-8", "az_AZ", "az_AZ.UTF-8", "az_IR", "az_IR.UTF-8", "be_BY", "be_BY.CP1251", "be_BY.UTF-8", "bem_ZM", "bem_ZM.UTF-8", "ber_DZ", "ber_DZ.UTF-8", "ber_MA", "ber_MA.UTF-8", "bg_BG", "bg_BG.CP1251", "bg_BG.UTF-8", "bho_IN", "bho_IN.UTF-8", "bho_NP", "bho_NP.UTF-8", "bi_VU", "bi_VU.UTF-8", "bn_BD", "bn_BD.UTF-8", "bn_IN", "bn_IN.UTF-8", "bo_CN", "bo_CN.UTF-8", "bo_IN", "bo_IN.UTF-8", "br_FR", "br_FR.ISO-8859-1", "br_FR.UTF-8", "brx_IN", "brx_IN.UTF-8", "bs_BA", "bs_BA.ISO-8859-2", "bs_BA.UTF-8", "byn_ER", "byn_ER.UTF-8", "ca_AD", "ca_AD.ISO-8859-15", "ca_AD.UTF-8", "ca_ES", "ca_ES.ISO-8859-1", "ca_ES.UTF-8", "ca_FR", "ca_FR.ISO-8859-15", "ca_FR.UTF-8", "ca_IT", "ca_IT.ISO-8859-15", "ca_IT.UTF-8", "ce_RU", "ce_RU.UTF-8", "chr_US", "chr_US.UTF-8", "ckb_IQ", "ckb_IQ.UTF-8", "cmn_TW", "cmn_TW.UTF-8", "crh_UA", "crh_UA.UTF-8", "csb_PL", "csb_PL.UTF-8", "cs_CZ", "cs_CZ.ISO-8859-2", "cs_CZ.UTF-8", "cv_RU", "cv_RU.UTF-8", "cy_GB", "cy_GB.ISO-8859-14", "cy_GB.UTF-8", "da_DK", "da_DK.ISO-8859-1", "da_DK.UTF-8", "de_AT", "de_AT.ISO-8859-1", "de_AT.UTF-8", "de_BE", "de_BE.ISO-8859-1", "de_BE.UTF-8", "de_CH", "de_CH.ISO-8859-1", "de_CH.UTF-8", "de_DE", "de_DE.ISO-8859-1", "de_DE.UTF-8", "de_IT", "de_IT.ISO-8859-1", "de_IT.UTF-8", "de_LU", "de_LU.ISO-8859-1", "de_LU.UTF-8", "doi_IN", "doi_IN.UTF-8", "dv_MV", "dv_MV.UTF-8", "dz_BT", "dz_BT.UTF-8", "el_CY", "el_CY.ISO-8859-7", "el_CY.UTF-8", "el_GR", "el_GR.ISO-8859-7", "el_GR.UTF-8", "en_AG", "en_AG.UTF-8", "en_AU", "en_AU.ISO-8859-1", "en_AU.UTF-8", "en_BW", "en_BW.ISO-8859-1", "en_BW.UTF-8", "en_CA", "en_CA.ISO-8859-1", "en_CA.UTF-8", "en_DK", "en_DK.ISO-8859-1", "en_DK.ISO-8859-15", "en_DK.UTF-8", "en_GB", "en_GB.ISO-8859-1", "en_GB.ISO-8859-15", "en_GB.UTF-8", "en_HK", "en_HK.ISO-8859-1", "en_HK.UTF-8", "en_IE", "en_IE.ISO-8859-1", "en_IE.UTF-8", "en_IL", "en_IL.UTF-8", "en_IN", "en_IN.UTF-8", "en_NG", "en_NG.UTF-8", "en_NZ", "en_NZ.ISO-8859-1", "en_NZ.UTF-8", "en_PH", "en_PH.ISO-8859-1", "en_PH.UTF-8", "en_SG", "en_SG.ISO-8859-1", "en_SG.UTF-8", "en_US", "en_US.ISO-8859-1", "en_US.ISO-8859-15", "en_US.UTF-8", "en_ZA", "en_ZA.ISO-8859-1", "en_ZA.UTF-8", "en_ZM", "en_ZM.UTF-8", "en_ZW", "en_ZW.ISO-8859-1", "en_ZW.UTF-8", "es_AR", "es_AR.ISO-8859-1", "es_AR.UTF-8", "es_BO", "es_BO.ISO-8859-1", "es_BO.UTF-8", "es_CL", "es_CL.ISO-8859-1", "es_CL.UTF-8", "es_CO", "es_CO.ISO-8859-1", "es_CO.UTF-8", "es_CR", "es_CR.ISO-8859-1", "es_CR.UTF-8", "es_CU", "es_CU.UTF-8", "es_DO", "es_DO.ISO-8859-1", "es_DO.UTF-8", "es_EC", "es_EC.ISO-8859-1", "es_EC.UTF-8", "es_ES", "es_ES.ISO-8859-1", "es_ES.UTF-8", "es_GT", "es_GT.ISO-8859-1", "es_GT.UTF-8", "es_HN", "es_HN.ISO-8859-1", "es_HN.UTF-8", "es_MX", "es_MX.ISO-8859-1", "es_MX.UTF-8", "es_NI", "es_NI.ISO-8859-1", "es_NI.UTF-8", "es_PA", "es_PA.ISO-8859-1", "es_PA.UTF-8", "es_PE", "es_PE.ISO-8859-1", "es_PE.UTF-8", "es_PR", "es_PR.ISO-8859-1", "es_PR.UTF-8", "es_PY", "es_PY.ISO-8859-1", "es_PY.UTF-8", "es_SV", "es_SV.ISO-8859-1", "es_SV.UTF-8", "es_US", "es_US.ISO-8859-1", "es_US.UTF-8", "es_UY", "es_UY.ISO-8859-1", "es_UY.UTF-8", "es_VE", "es_VE.ISO-8859-1", "es_VE.UTF-8", "et_EE", "et_EE.ISO-8859-1", "et_EE.ISO-8859-15", "et_EE.UTF-8", "eu_ES", "eu_ES.ISO-8859-1", "eu_ES.UTF-8", "eu_FR", "eu_FR.ISO-8859-1", "eu_FR.UTF-8", "fa_IR", "fa_IR.UTF-8", "ff_SN", "ff_SN.UTF-8", "fi_FI", "fi_FI.ISO-8859-1", "fi_FI.UTF-8", "fil_PH", "fil_PH.UTF-8", "fo_FO", "fo_FO.ISO-8859-1", "fo_FO.UTF-8", "fr_BE", "fr_BE.ISO-8859-1", "fr_BE.UTF-8", "fr_CA", "fr_CA.ISO-8859-1", "fr_CA.UTF-8", "fr_CH", "fr_CH.ISO-8859-1", "fr_CH.UTF-8", "fr_FR", "fr_FR.ISO-8859-1", "fr_FR.UTF-8", "fr_LU", "fr_LU.ISO-8859-1", "fr_LU.UTF-8", "fur_IT", "fur_IT.UTF-8", "fy_DE", "fy_DE.UTF-8", "fy_NL", "fy_NL.UTF-8", "ga_IE", "ga_IE.ISO-8859-1", "ga_IE.UTF-8", "gd_GB", "gd_GB.ISO-8859-15", "gd_GB.UTF-8", "gez_ER", "gez_ER.UTF-8", "gez_ET", "gez_ET.UTF-8", "gl_ES", "gl_ES.ISO-8859-1", "gl_ES.UTF-8", "gu_IN", "gu_IN.UTF-8", "gv_GB", "gv_GB.ISO-8859-1", "gv_GB.UTF-8", "hak_TW", "hak_TW.UTF-8", "ha_NG", "ha_NG.UTF-8", "he_IL", "he_IL.ISO-8859-8", "he_IL.UTF-8", "hif_FJ", "hif_FJ.UTF-8", "hi_IN", "hi_IN.UTF-8", "hne_IN", "hne_IN.UTF-8", "hr_HR", "hr_HR.ISO-8859-2", "hr_HR.UTF-8", "hsb_DE", "hsb_DE.ISO-8859-2", "hsb_DE.UTF-8", "ht_HT", "ht_HT.UTF-8", "hu_HU", "hu_HU.ISO-8859-2", "hu_HU.UTF-8", "hy_AM", "hy_AM.ARMSCII-8", "hy_AM.UTF-8", "ia_FR", "ia_FR.UTF-8", "id_ID", "id_ID.ISO-8859-1", "id_ID.UTF-8", "ig_NG", "ig_NG.UTF-8", "ik_CA", "ik_CA.UTF-8", "is_IS", "is_IS.ISO-8859-1", "is_IS.UTF-8", "it_CH", "it_CH.ISO-8859-1", "it_CH.UTF-8", "it_IT", "it_IT.ISO-8859-1", "it_IT.UTF-8", "iu_CA", "iu_CA.UTF-8", "kab_DZ", "kab_DZ.UTF-8", "ka_GE", "ka_GE.GEORGIAN-PS", "ka_GE.UTF-8", "kk_KZ", "kk_KZ.PT154", "kk_KZ.RK1048", "kk_KZ.UTF-8", "kl_GL", "kl_GL.ISO-8859-1", "kl_GL.UTF-8", "km_KH", "km_KH.UTF-8", "kn_IN", "kn_IN.UTF-8", "kok_IN", "kok_IN.UTF-8", "ks_IN", "ks_IN.UTF-8", "ku_TR", "ku_TR.ISO-8859-9", "ku_TR.UTF-8", "kw_GB", "kw_GB.ISO-8859-1", "kw_GB.UTF-8", "ky_KG", "ky_KG.UTF-8", "lb_LU", "lb_LU.UTF-8", "lg_UG", "lg_UG.ISO-8859-10", "lg_UG.UTF-8", "li_BE", "li_BE.UTF-8", "lij_IT", "lij_IT.UTF-8", "li_NL", "li_NL.UTF-8", "ln_CD", "ln_CD.UTF-8", "lo_LA", "lo_LA.UTF-8", "lt_LT", "lt_LT.ISO-8859-13", "lt_LT.UTF-8", "lv_LV", "lv_LV.ISO-8859-13", "lv_LV.UTF-8", "lzh_TW", "lzh_TW.UTF-8", "mag_IN", "mag_IN.UTF-8", "mai_IN", "mai_IN.UTF-8", "mai_NP", "mai_NP.UTF-8", "mfe_MU", "mfe_MU.UTF-8", "mg_MG", "mg_MG.ISO-8859-15", "mg_MG.UTF-8", "mhr_RU", "mhr_RU.UTF-8", "mi_NZ", "mi_NZ.ISO-8859-13", "mi_NZ.UTF-8", "miq_NI", "miq_NI.UTF-8", "mjw_IN", "mjw_IN.UTF-8", "mk_MK", "mk_MK.ISO-8859-5", "mk_MK.UTF-8", "ml_IN", "ml_IN.UTF-8", "mni_IN", "mni_IN.UTF-8", "mn_MN", "mn_MN.UTF-8", "mr_IN", "mr_IN.UTF-8", "ms_MY", "ms_MY.ISO-8859-1", "ms_MY.UTF-8", "mt_MT", "mt_MT.ISO-8859-3", "mt_MT.UTF-8", "my_MM", "my_MM.UTF-8", "nan_TW", "nan_TW.UTF-8", "nb_NO", "nb_NO.ISO-8859-1", "nb_NO.UTF-8", "nds_DE", "nds_DE.UTF-8", "nds_NL", "nds_NL.UTF-8", "ne_NP", "ne_NP.UTF-8", "nhn_MX", "nhn_MX.UTF-8", "niu_NU", "niu_NU.UTF-8", "niu_NZ", "niu_NZ.UTF-8", "nl_AW", "nl_AW.UTF-8", "nl_BE", "nl_BE.ISO-8859-1", "nl_BE.UTF-8", "nl_NL", "nl_NL.ISO-8859-1", "nl_NL.UTF-8", "nn_NO", "nn_NO.ISO-8859-1", "nn_NO.UTF-8", "nr_ZA", "nr_ZA.UTF-8", "nso_ZA", "nso_ZA.UTF-8", "oc_FR", "oc_FR.ISO-8859-1", "oc_FR.UTF-8", "om_ET", "om_ET.UTF-8", "om_KE", "om_KE.ISO-8859-1", "om_KE.UTF-8", "or_IN", "or_IN.UTF-8", "os_RU", "os_RU.UTF-8", "pa_IN", "pa_IN.UTF-8", "pap_AW", "pap_AW.UTF-8", "pap_CW", "pap_CW.UTF-8", "pa_PK", "pa_PK.UTF-8", "pl_PL", "pl_PL.ISO-8859-2", "pl_PL.UTF-8", "ps_AF", "ps_AF.UTF-8", "pt_BR", "pt_BR.ISO-8859-1", "pt_BR.UTF-8", "pt_PT", "pt_PT.ISO-8859-1", "pt_PT.UTF-8", "quz_PE", "quz_PE.UTF-8", "raj_IN", "raj_IN.UTF-8", "ro_RO", "ro_RO.ISO-8859-2", "ro_RO.UTF-8", "ru_RU", "ru_RU.CP1251", "ru_RU.ISO-8859-5", "ru_RU.KOI8-R", "ru_RU.UTF-8", "ru_UA", "ru_UA.KOI8-U", "ru_UA.UTF-8", "rw_RW", "rw_RW.UTF-8", "sa_IN", "sa_IN.UTF-8", "sat_IN", "sat_IN.UTF-8", "sc_IT", "sc_IT.UTF-8", "sd_IN", "sd_IN.UTF-8", "sd_PK", "sd_PK.UTF-8", "se_NO", "se_NO.UTF-8", "sgs_LT", "sgs_LT.UTF-8", "shn_MM", "shn_MM.UTF-8", "shs_CA", "shs_CA.UTF-8", "sid_ET", "sid_ET.UTF-8", "si_LK", "si_LK.UTF-8", "sk_SK", "sk_SK.ISO-8859-2", "sk_SK.UTF-8", "sl_SI", "sl_SI.ISO-8859-2", "sl_SI.UTF-8", "sm_WS", "sm_WS.UTF-8", "so_DJ", "so_DJ.ISO-8859-1", "so_DJ.UTF-8", "so_ET", "so_ET.UTF-8", "so_KE", "so_KE.ISO-8859-1", "so_KE.UTF-8", "so_SO", "so_SO.ISO-8859-1", "so_SO.UTF-8", "sq_AL", "sq_AL.ISO-8859-1", "sq_AL.UTF-8", "sq_MK", "sq_MK.UTF-8", "sr_ME", "sr_ME.UTF-8", "sr_RS", "sr_RS.UTF-8", "ss_ZA", "ss_ZA.UTF-8", "st_ZA", "st_ZA.ISO-8859-1", "st_ZA.UTF-8", "sv_FI", "sv_FI.ISO-8859-1", "sv_FI.UTF-8", "sv_SE", "sv_SE.ISO-8859-1", "sv_SE.ISO-8859-15", "sv_SE.UTF-8", "sw_KE", "sw_KE.UTF-8", "sw_TZ", "sw_TZ.UTF-8", "szl_PL", "szl_PL.UTF-8", "ta_IN", "ta_IN.UTF-8", "ta_LK", "ta_LK.UTF-8", "te_IN", "te_IN.UTF-8", "tg_TJ", "tg_TJ.KOI8-T", "tg_TJ.UTF-8", "the_NP", "the_NP.UTF-8", "th_TH", "th_TH.TIS-620", "th_TH.UTF-8", "ti_ER", "ti_ER.UTF-8", "ti_ET", "ti_ET.UTF-8", "tig_ER", "tig_ER.UTF-8", "tk_TM", "tk_TM.UTF-8", "tl_PH", "tl_PH.ISO-8859-1", "tl_PH.UTF-8", "tn_ZA", "tn_ZA.UTF-8", "to_TO", "to_TO.UTF-8", "tpi_PG", "tpi_PG.UTF-8", "tr_CY", "tr_CY.ISO-8859-9", "tr_CY.UTF-8", "tr_TR", "tr_TR.ISO-8859-9", "tr_TR.UTF-8", "ts_ZA", "ts_ZA.UTF-8", "tt_RU", "tt_RU.UTF-8", "ug_CN", "ug_CN.UTF-8", "uk_UA", "uk_UA.KOI8-U", "uk_UA.UTF-8", "unm_US", "unm_US.UTF-8", "ur_IN", "ur_IN.UTF-8", "ur_PK", "ur_PK.UTF-8", "uz_UZ", "uz_UZ.ISO-8859-1", "uz_UZ.UTF-8", "ve_ZA", "ve_ZA.UTF-8", "vi_VN", "vi_VN.UTF-8", "wa_BE", "wa_BE.ISO-8859-1", "wa_BE.UTF-8", "wae_CH", "wae_CH.UTF-8", "wal_ET", "wal_ET.UTF-8", "wo_SN", "wo_SN.UTF-8", "xh_ZA", "xh_ZA.ISO-8859-1", "xh_ZA.UTF-8", "yi_US", "yi_US.CP1255", "yi_US.UTF-8", "yo_NG", "yo_NG.UTF-8", "yue_HK", "yue_HK.UTF-8", "yuw_PG", "yuw_PG.UTF-8", "zh_CN", "zh_CN.GB18030", "zh_CN.GB2312", "zh_CN.GBK", "zh_CN.UTF-8", "zh_HK", "zh_HK.BIG5-HKSCS", "zh_HK.UTF-8", "zh_SG", "zh_SG.GB2312", "zh_SG.GBK", "zh_SG.UTF-8", "zh_TW", "zh_TW.BIG5", "zh_TW.EUC-TW", "zh_TW.UTF-8", "zu_ZA", "zu_ZA.ISO-8859-1", "zu_ZA.UTF-8" +}; + +std::string ConsumeLocaleIdentifier(FuzzedDataProvider& fuzzed_data_provider) +{ + return fuzzed_data_provider.PickValueInArray<std::string>(locale_identifiers); +} + +bool IsAvailableLocale(const std::string& locale_identifier) +{ + try { + (void)std::locale(locale_identifier); + } catch (const std::runtime_error&) { + return false; + } + return true; +} +} // namespace + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const std::string locale_identifier = ConsumeLocaleIdentifier(fuzzed_data_provider); + if (!IsAvailableLocale(locale_identifier)) { + return; + } + const char* c_locale = std::setlocale(LC_ALL, "C"); + assert(c_locale != nullptr); + + const std::string random_string = fuzzed_data_provider.ConsumeRandomLengthString(5); + int32_t parseint32_out_without_locale; + const bool parseint32_without_locale = ParseInt32(random_string, &parseint32_out_without_locale); + int64_t parseint64_out_without_locale; + const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale); + const int64_t atoi64_without_locale = atoi64(random_string); + const int atoi_without_locale = atoi(random_string); + const int64_t atoi64c_without_locale = atoi64(random_string.c_str()); + const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + const std::string i64tostr_without_locale = i64tostr(random_int64); + const int32_t random_int32 = fuzzed_data_provider.ConsumeIntegral<int32_t>(); + const std::string itostr_without_locale = itostr(random_int32); + const std::string strprintf_int_without_locale = strprintf("%d", random_int64); + const double random_double = fuzzed_data_provider.ConsumeFloatingPoint<double>(); + const std::string strprintf_double_without_locale = strprintf("%f", random_double); + + const char* new_locale = std::setlocale(LC_ALL, locale_identifier.c_str()); + assert(new_locale != nullptr); + + int32_t parseint32_out_with_locale; + const bool parseint32_with_locale = ParseInt32(random_string, &parseint32_out_with_locale); + assert(parseint32_without_locale == parseint32_with_locale); + if (parseint32_without_locale) { + assert(parseint32_out_without_locale == parseint32_out_with_locale); + } + int64_t parseint64_out_with_locale; + const bool parseint64_with_locale = ParseInt64(random_string, &parseint64_out_with_locale); + assert(parseint64_without_locale == parseint64_with_locale); + if (parseint64_without_locale) { + assert(parseint64_out_without_locale == parseint64_out_with_locale); + } + const int64_t atoi64_with_locale = atoi64(random_string); + assert(atoi64_without_locale == atoi64_with_locale); + const int64_t atoi64c_with_locale = atoi64(random_string.c_str()); + assert(atoi64c_without_locale == atoi64c_with_locale); + const int atoi_with_locale = atoi(random_string); + assert(atoi_without_locale == atoi_with_locale); + const std::string i64tostr_with_locale = i64tostr(random_int64); + assert(i64tostr_without_locale == i64tostr_with_locale); + const std::string itostr_with_locale = itostr(random_int32); + assert(itostr_without_locale == itostr_with_locale); + const std::string strprintf_int_with_locale = strprintf("%d", random_int64); + assert(strprintf_int_without_locale == strprintf_int_with_locale); + const std::string strprintf_double_with_locale = strprintf("%f", random_double); + assert(strprintf_double_without_locale == strprintf_double_with_locale); + + const std::locale current_cpp_locale; + assert(current_cpp_locale == std::locale::classic()); +} diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp index 0de21f0e7c..d5be1070bd 100644 --- a/src/test/fuzz/strprintf.cpp +++ b/src/test/fuzz/strprintf.cpp @@ -8,7 +8,6 @@ #include <util/strencodings.h> #include <algorithm> -#include <cassert> #include <cstdint> #include <string> #include <vector> @@ -27,7 +26,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) // * strprintf("%.222222200000000$", 1.1); // // Upstream bug report: https://github.com/c42f/tinyformat/issues/70 - if (format_string.find("%") != std::string::npos && digits_in_format_specifier >= 7) { + if (format_string.find('%') != std::string::npos && digits_in_format_specifier >= 7) { return; } @@ -35,7 +34,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) // * strprintf("%1$*1$*", -11111111); // // Upstream bug report: https://github.com/c42f/tinyformat/issues/70 - if (format_string.find("%") != std::string::npos && format_string.find("$") != std::string::npos && format_string.find("*") != std::string::npos && digits_in_format_specifier > 0) { + if (format_string.find('%') != std::string::npos && format_string.find('$') != std::string::npos && format_string.find('*') != std::string::npos && digits_in_format_specifier > 0) { return; } @@ -96,7 +95,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) } try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 13)) { + switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) { case 0: (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); break; @@ -115,32 +114,52 @@ void test_one_input(const std::vector<uint8_t>& buffer) case 5: (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool()); break; - case 6: + } + } catch (const tinyformat::format_error&) { + } + + if (format_string.find('%') != std::string::npos && format_string.find('c') != std::string::npos) { + // Avoid triggering the following: + // * strprintf("%c", 1.31783e+38); + // tinyformat.h:244:36: runtime error: 1.31783e+38 is outside the range of representable values of type 'char' + return; + } + + if (format_string.find('%') != std::string::npos && format_string.find('*') != std::string::npos) { + // Avoid triggering the following: + // * strprintf("%*", -2.33527e+38); + // tinyformat.h:283:65: runtime error: -2.33527e+38 is outside the range of representable values of type 'int' + // * strprintf("%*", -2147483648); + // tinyformat.h:763:25: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself + return; + } + + try { + switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) { + case 0: (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>()); break; - case 7: + case 1: (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>()); break; - case 8: + case 2: (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>()); break; - case 9: + case 3: (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>()); break; - case 10: + case 4: (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>()); break; - case 11: + case 5: (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); break; - case 12: + case 6: (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>()); break; - case 13: + case 7: (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>()); break; - default: - assert(false); } } catch (const tinyformat::format_error&) { } diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp index 532fe143ae..4e51b8c02a 100644 --- a/src/test/reverselock_tests.cpp +++ b/src/test/reverselock_tests.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <reverselock.h> +#include <sync.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> @@ -11,21 +11,50 @@ BOOST_FIXTURE_TEST_SUITE(reverselock_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(reverselock_basics) { - boost::mutex mutex; - boost::unique_lock<boost::mutex> lock(mutex); + Mutex mutex; + WAIT_LOCK(mutex, lock); BOOST_CHECK(lock.owns_lock()); { - reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + REVERSE_LOCK(lock); BOOST_CHECK(!lock.owns_lock()); } BOOST_CHECK(lock.owns_lock()); } +BOOST_AUTO_TEST_CASE(reverselock_multiple) +{ + Mutex mutex2; + Mutex mutex; + WAIT_LOCK(mutex2, lock2); + WAIT_LOCK(mutex, lock); + + // Make sure undoing two locks succeeds + { + REVERSE_LOCK(lock); + BOOST_CHECK(!lock.owns_lock()); + REVERSE_LOCK(lock2); + BOOST_CHECK(!lock2.owns_lock()); + } + BOOST_CHECK(lock.owns_lock()); + BOOST_CHECK(lock2.owns_lock()); +} + BOOST_AUTO_TEST_CASE(reverselock_errors) { - boost::mutex mutex; - boost::unique_lock<boost::mutex> lock(mutex); + Mutex mutex2; + Mutex mutex; + WAIT_LOCK(mutex2, lock2); + WAIT_LOCK(mutex, lock); + +#ifdef DEBUG_LOCKORDER + // Make sure trying to reverse lock a previous lock fails + try { + REVERSE_LOCK(lock2); + BOOST_CHECK(false); // REVERSE_LOCK(lock2) succeeded + } catch(...) { } + BOOST_CHECK(lock2.owns_lock()); +#endif // Make sure trying to reverse lock an unlocked lock fails lock.unlock(); @@ -34,7 +63,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors) bool failed = false; try { - reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + REVERSE_LOCK(lock); } catch(...) { failed = true; } @@ -49,7 +78,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors) lock.lock(); BOOST_CHECK(lock.owns_lock()); { - reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + REVERSE_LOCK(lock); BOOST_CHECK(!lock.owns_lock()); } diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 26a6452ffd..7d26840b73 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -4,37 +4,26 @@ #include <random.h> #include <scheduler.h> +#include <util/time.h> #include <boost/thread.hpp> #include <boost/test/unit_test.hpp> BOOST_AUTO_TEST_SUITE(scheduler_tests) -static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, boost::chrono::system_clock::time_point rescheduleTime) +static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime) { { boost::unique_lock<boost::mutex> lock(mutex); counter += delta; } - boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min(); + std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min(); if (rescheduleTime != noTime) { CScheduler::Function f = std::bind(µTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime); s.schedule(f, rescheduleTime); } } -static void MicroSleep(uint64_t n) -{ -#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) - boost::this_thread::sleep_for(boost::chrono::microseconds(n)); -#elif defined(HAVE_WORKING_BOOST_SLEEP) - boost::this_thread::sleep(boost::posix_time::microseconds(n)); -#else - //should never get here - #error missing boost sleep implementation -#endif -} - BOOST_AUTO_TEST_CASE(manythreads) { // Stress test: hundreds of microsecond-scheduled tasks, @@ -56,15 +45,15 @@ BOOST_AUTO_TEST_CASE(manythreads) auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] - boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); - boost::chrono::system_clock::time_point now = start; - boost::chrono::system_clock::time_point first, last; + std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point now = start; + std::chrono::system_clock::time_point first, last; size_t nTasks = microTasks.getQueueInfo(first, last); BOOST_CHECK(nTasks == 0); for (int i = 0; i < 100; ++i) { - boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); - boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); + std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); + std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), @@ -81,15 +70,15 @@ BOOST_AUTO_TEST_CASE(manythreads) for (int i = 0; i < 5; i++) microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); - MicroSleep(600); - now = boost::chrono::system_clock::now(); + UninterruptibleSleep(std::chrono::microseconds{600}); + now = std::chrono::system_clock::now(); // More threads and more tasks: for (int i = 0; i < 5; i++) microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); for (int i = 0; i < 100; i++) { - boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); - boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); + std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); + std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), @@ -153,7 +142,6 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) BOOST_CHECK_EQUAL(counter2, 100); } -/* disabled for now. See discussion in https://github.com/bitcoin/bitcoin/pull/18174 BOOST_AUTO_TEST_CASE(mockforward) { CScheduler scheduler; @@ -168,14 +156,14 @@ BOOST_AUTO_TEST_CASE(mockforward) scheduler.scheduleFromNow(dummy, 8*min_in_milli); // check taskQueue - boost::chrono::system_clock::time_point first, last; + std::chrono::system_clock::time_point first, last; size_t num_tasks = scheduler.getQueueInfo(first, last); BOOST_CHECK_EQUAL(num_tasks, 3ul); std::thread scheduler_thread([&]() { scheduler.serviceQueue(); }); // bump the scheduler forward 5 minutes - scheduler.MockForward(boost::chrono::seconds(5*60)); + scheduler.MockForward(std::chrono::seconds(5*60)); // ensure scheduler has chance to process all tasks queued for before 1 ms from now. scheduler.scheduleFromNow([&scheduler]{ scheduler.stop(false); }, 1); @@ -189,11 +177,10 @@ BOOST_AUTO_TEST_CASE(mockforward) BOOST_CHECK_EQUAL(counter, 2); // check that the time of the remaining job has been updated - boost::chrono::system_clock::time_point now = boost::chrono::system_clock::now(); - int delta = boost::chrono::duration_cast<boost::chrono::seconds>(first - now).count(); + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count(); // should be between 2 & 3 minutes from now BOOST_CHECK(delta > 2*60 && delta < 3*60); } -*/ BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp index 4b0214a15a..3550a02316 100644 --- a/src/test/txindex_tests.cpp +++ b/src/test/txindex_tests.cpp @@ -34,7 +34,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) int64_t time_start = GetTimeMillis(); while (!txindex.BlockUntilSyncedToCurrentChain()) { BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis()); - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } // Check that txindex excludes genesis block transactions. @@ -70,6 +70,8 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) // shutdown sequence (c.f. Shutdown() in init.cpp) txindex.Stop(); + // txindex job may be scheduled, so stop scheduler before destructing + m_node.scheduler->stop(); threadGroup.interrupt_all(); threadGroup.join_all(); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index fc736bc3a1..53eb9ff43b 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -140,6 +140,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha TestingSetup::~TestingSetup() { + if (m_node.scheduler) m_node.scheduler->stop(); threadGroup.interrupt_all(); threadGroup.join_all(); GetMainSignals().FlushBackgroundCallbacks(); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 536ff3ba25..eb0d31fbdc 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1333,7 +1333,7 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime) SetMockTime(111); // Check that mock time does not change after a sleep for (const auto& num_sleep : {0, 1}) { - MilliSleep(num_sleep); + UninterruptibleSleep(std::chrono::milliseconds{num_sleep}); BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count()); BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count()); @@ -1344,7 +1344,7 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime) // Check that system time changes after a sleep const auto ms_0 = GetTime<std::chrono::milliseconds>(); const auto us_0 = GetTime<std::chrono::microseconds>(); - MilliSleep(1); + UninterruptibleSleep(std::chrono::milliseconds{1}); BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>()); BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>()); } diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index dae389a167..f2c862011d 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) t.join(); } while (GetMainSignals().CallbacksPending() > 0) { - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } UnregisterValidationInterface(&sub); diff --git a/src/txmempool.h b/src/txmempool.h index de11d626b4..0de6f28777 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -753,7 +753,7 @@ public: * determine if that transaction has not yet been visited during the current * traversal's epoch. * Algorithms using std::set can be replaced on a one by one basis. - * Both techniques are not fundamentally incomaptible across the codebase. + * Both techniques are not fundamentally incompatible across the codebase. * Generally speaking, however, the remaining use of std::set for mempool * traversal should be viewed as a TODO for replacement with an epoch based * traversal, rather than a preference for std::set over epochs in that diff --git a/src/util/time.cpp b/src/util/time.cpp index f33966f149..14937b985e 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -11,10 +11,13 @@ #include <atomic> #include <boost/date_time/posix_time/posix_time.hpp> -#include <boost/thread.hpp> #include <ctime> +#include <thread> + #include <tinyformat.h> +void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); } + static std::atomic<int64_t> nMockTime(0); //!< For unit testing int64_t GetTime() @@ -72,24 +75,6 @@ int64_t GetSystemTimeInSeconds() return GetTimeMicros()/1000000; } -void MilliSleep(int64_t n) -{ - -/** - * Boost's sleep_for was uninterruptible when backed by nanosleep from 1.50 - * until fixed in 1.52. Use the deprecated sleep method for the broken case. - * See: https://svn.boost.org/trac/boost/ticket/7238 - */ -#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) - boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); -#elif defined(HAVE_WORKING_BOOST_SLEEP) - boost::this_thread::sleep(boost::posix_time::milliseconds(n)); -#else -//should never get here -#error missing boost sleep implementation -#endif -} - std::string FormatISO8601DateTime(int64_t nTime) { struct tm ts; time_t time_val = nTime; diff --git a/src/util/time.h b/src/util/time.h index af4390aa1c..77de1e047d 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -10,6 +10,8 @@ #include <string> #include <chrono> +void UninterruptibleSleep(const std::chrono::microseconds& n); + /** * Helper to count the seconds of a duration. * @@ -36,8 +38,6 @@ void SetMockTime(int64_t nMockTimeIn); /** For testing */ int64_t GetMockTime(); -void MilliSleep(int64_t n); - /** Return system time (or mocked time, if set) */ template <typename T> T GetTime(); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 043e29f9cf..67be4d85d2 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -756,7 +756,7 @@ bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip) return fSuccess; } } - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } } @@ -887,7 +887,7 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const } } } - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 19180f1f30..8df3491060 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -688,7 +688,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request) {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"}, }, RPCResult{ - "\"key\" (string) The private key\n" + RPCResult::Type::STR, "key", "The private key" }, RPCExamples{ HelpExampleCli("dumpprivkey", "\"myaddress\"") @@ -738,9 +738,10 @@ UniValue dumpwallet(const JSONRPCRequest& request) {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (either absolute or relative to bitcoind)"}, }, RPCResult{ - "{ (json object)\n" - " \"filename\" : { (string) The filename with full absolute path\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "filename", "The filename with full absolute path"}, + } }, RPCExamples{ HelpExampleCli("dumpwallet", "\"test\"") @@ -1321,19 +1322,21 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) "\"options\""}, }, RPCResult{ - "[ (json array) Response is an array with the same size as the input that has the execution result\n" - " { (json object)\n" - " \"success\" : true|false, (boolean)\n" - " \"warnings\" : [ (json array, optional)\n" - " \"str\", (string)\n" - " ...\n" - " ],\n" - " \"error\" : { (json object, optional)\n" - " ... JSONRPC error\n" - " },\n" - " },\n" - " ...\n" - "]\n" + RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "success", ""}, + {RPCResult::Type::ARR, "warnings", /* optional */ true, "", + { + {RPCResult::Type::STR, "", ""}, + }}, + {RPCResult::Type::OBJ, "error", /* optional */ true, "", + { + {RPCResult::Type::ELISION, "", "JSONRPC error"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, " diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 78281053c0..a390050f9a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -24,6 +24,7 @@ #include <util/string.h> #include <util/system.h> #include <util/url.h> +#include <util/vector.h> #include <wallet/coincontrol.h> #include <wallet/feebumper.h> #include <wallet/psbtwallet.h> @@ -205,7 +206,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request) {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, }, RPCResult{ - "\"address\" (string) The new bitcoin address\n" + RPCResult::Type::STR, "address", "The new bitcoin address" }, RPCExamples{ HelpExampleCli("getnewaddress", "") @@ -256,7 +257,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request) {"address_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, }, RPCResult{ - "\"address\" (string) The address\n" + RPCResult::Type::STR, "address", "The address" }, RPCExamples{ HelpExampleCli("getrawchangeaddress", "") @@ -390,7 +391,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request) " dirty if they have previously been used in a transaction."}, }, RPCResult{ - "\"txid\" (string) The transaction id.\n" + RPCResult::Type::STR_HEX, "txid", "The transaction id." }, RPCExamples{ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") @@ -469,17 +470,18 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request) "in past transactions\n", {}, RPCResult{ - "[\n" - " [\n" - " [\n" - " \"address\", (string) The bitcoin address\n" - " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"label\" (string, optional) The label\n" - " ]\n" - " ,...\n" - " ]\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR, "address", "The bitcoin address"}, + {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT}, + {RPCResult::Type::STR, "label", /* optional */ true, "The label"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("listaddressgroupings", "") @@ -532,7 +534,7 @@ static UniValue signmessage(const JSONRPCRequest& request) {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."}, }, RPCResult{ - "\"signature\" (string) The signature of the message encoded in base 64\n" + RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64" }, RPCExamples{ "\nUnlock the wallet for 30 seconds\n" @@ -601,7 +603,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."}, }, RPCResult{ - "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" + RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address." }, RPCExamples{ "\nThe amount from transactions with at least 1 confirmation\n" @@ -671,7 +673,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."}, }, RPCResult{ - "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n" + RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label." }, RPCExamples{ "\nAmount received by the default label with at least 1 confirmation\n" @@ -743,7 +745,7 @@ static UniValue getbalance(const JSONRPCRequest& request) {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."}, }, RPCResult{ - "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n" + RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet." }, RPCExamples{ "\nThe total amount in the wallet with 1 or more confirmations\n" @@ -845,8 +847,8 @@ static UniValue sendmany(const JSONRPCRequest& request) " \"CONSERVATIVE\""}, }, RPCResult{ - "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" - " the number of addresses.\n" + RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n" + "the number of addresses." }, RPCExamples{ "\nSend two amounts to two different addresses:\n" @@ -969,11 +971,12 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, }, 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" - " \"descriptor\" : \"descriptor\" (string) The descriptor for this multisig\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The value of the new multisig address"}, + {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"}, + {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, + } }, RPCExamples{ "\nAdd a multisig address from 2 addresses\n" @@ -1202,20 +1205,21 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request) {"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."}, }, RPCResult{ - "[\n" - " {\n" - " \"involvesWatchonly\" : true, (boolean) Only returns true if imported addresses were involved in transaction.\n" - " \"address\" : \"receivingaddress\", (string) The receiving address\n" - " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" - " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" - " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n" - " \"txids\" : [\n" - " \"txid\", (string) The ids of transactions received with the address \n" - " ...\n" - " ]\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"}, + {RPCResult::Type::STR, "address", "The receiving address"}, + {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"}, + {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""}, + {RPCResult::Type::ARR, "txids", "", + { + {RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("listreceivedbyaddress", "") @@ -1252,15 +1256,16 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request) {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"}, }, RPCResult{ - "[\n" - " {\n" - " \"involvesWatchonly\" : true, (boolean) Only returns true if imported addresses were involved in transaction.\n" - " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n" - " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" - " \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"}, + {RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"}, + {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""}, + }}, + } }, RPCExamples{ HelpExampleCli("listreceivedbylabel", "") @@ -1372,26 +1377,26 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle } } -static const std::string TransactionDescriptionString() +static const std::vector<RPCResult> TransactionDescriptionString() { - return " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Negative confirmations means the\n" - " transaction conflicted that many blocks ago.\n" - " \"generated\" : xxx, (boolean) Only present if transaction only input is a coinbase one.\n" - " \"trusted\" : xxx, (boolean) Only present if we consider transaction to be trusted and so safe to spend from.\n" - " \"blockhash\" : \"hashvalue\", (string) The block hash containing the transaction.\n" - " \"blockheight\" : n, (numeric) The block height containing the transaction.\n" - " \"blockindex\" : n, (numeric) The index of the transaction in the block that includes it.\n" - " \"blocktime\" : xxx, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"txid\" : \"transactionid\", (string) The transaction id.\n" - " \"walletconflicts\" : [ (json array) Conflicting transaction ids.\n" - " \"txid\", (string) The transaction id.\n" - " ...\n" - " ],\n" - " \"time\" : xxx, (numeric) The transaction time expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"timereceived\" : xxx, (numeric) The time received expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"comment\" : \"...\", (string) If a comment is associated with the transaction, only present if not empty.\n" - " \"bip125-replaceable\" : \"str\", (string) (\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" - " may be unknown for unconfirmed transactions not in the mempool\n"; + return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n" + "transaction conflicted that many blocks ago."}, + {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."}, + {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."}, + {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."}, + {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."}, + {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."}, + {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."}, + {RPCResult::Type::STR_HEX, "txid", "The transaction id."}, + {RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id."}, + }}, + {RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."}, + {RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."}, + {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."}, + {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + "may be unknown for unconfirmed transactions not in the mempool"}}; } UniValue listtransactions(const JSONRPCRequest& request) @@ -1414,27 +1419,31 @@ UniValue listtransactions(const JSONRPCRequest& request) {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"}, }, RPCResult{ - "[\n" - " {\n" - " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n" - " \"address\" : \"address\", (string) The bitcoin address of the transaction.\n" - " \"category\" : (string) The transaction category.\n" - " \"send\" Transactions sent.\n" - " \"receive\" Non-coinbase transactions received.\n" - " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" - " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" - " \"orphan\" Orphaned coinbase transactions received.\n" - " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" - " for all other categories\n" - " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" - " \"vout\" : n, (numeric) the vout value\n" - " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" - " 'send' category of transactions.\n" - + TransactionDescriptionString() - + " \"abandoned\": xxx (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" - " 'send' category of transactions.\n" - " }\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>( + { + {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."}, + {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."}, + {RPCResult::Type::STR, "category", "The transaction category.\n" + "\"send\" Transactions sent.\n" + "\"receive\" Non-coinbase transactions received.\n" + "\"generate\" Coinbase transactions received with more than 100 confirmations.\n" + "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" + "\"orphan\" Orphaned coinbase transactions received."}, + {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" + "for all other categories"}, + {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"}, + {RPCResult::Type::NUM, "vout", "the vout value"}, + {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n" + "'send' category of transactions."}, + }, + TransactionDescriptionString()), + { + {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + "'send' category of transactions."}, + })}, + } }, RPCExamples{ "\nList the most recent 10 transactions in the systems\n" @@ -1525,34 +1534,39 @@ static UniValue listsinceblock(const JSONRPCRequest& request) " (not guaranteed to work on pruned nodes)"}, }, RPCResult{ - "{ (json object)\n" - " \"transactions\" : [ (json array)\n" - " { (json object)\n" - " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n" - " \"address\" : \"str\", (string) The bitcoin address of the transaction.\n" - " \"category\" : \"str\", (string) The transaction category.\n" - " \"send\" Transactions sent.\n" - " \"receive\" Non-coinbase transactions received.\n" - " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" - " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" - " \"orphan\" Orphaned coinbase transactions received.\n" - " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" - " for all other categories\n" - " \"vout\" : n, (numeric) the vout value\n" - " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" - + TransactionDescriptionString() - + " \"abandoned\": xxx, (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" - " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" - " \"to\" : \"...\", (string) If a comment to is associated with the transaction.\n" - " },\n" - " ...\n" - " ],\n" - " \"removed\" : [ (json array)\n" - " <structure is the same as \"transactions\" above, only present if include_removed=true>\n" - " Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count.\n" - " ],\n" - " \"lastblock\" : \"hex\" (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" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "transactions", "", + { + {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>( + { + {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."}, + {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."}, + {RPCResult::Type::STR, "category", "The transaction category.\n" + "\"send\" Transactions sent.\n" + "\"receive\" Non-coinbase transactions received.\n" + "\"generate\" Coinbase transactions received with more than 100 confirmations.\n" + "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" + "\"orphan\" Orphaned coinbase transactions received."}, + {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" + "for all other categories"}, + {RPCResult::Type::NUM, "vout", "the vout value"}, + {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n" + "'send' category of transactions."}, + }, + TransactionDescriptionString()), + { + {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + "'send' category of transactions."}, + {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"}, + {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."}, + })}, + }}, + {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n" + "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count." + , {{RPCResult::Type::ELISION, "", ""},}}, + {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"}, + } }, RPCExamples{ HelpExampleCli("listsinceblock", "") @@ -1660,35 +1674,41 @@ static UniValue gettransaction(const JSONRPCRequest& request) "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"}, }, 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" - " 'send' category of transactions.\n" - + TransactionDescriptionString() - + " \"details\" : [\n" - " {\n" - " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n" - " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" - " \"category\" : (string) The transaction category.\n" - " \"send\" Transactions sent.\n" - " \"receive\" Non-coinbase transactions received.\n" - " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" - " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" - " \"orphan\" Orphaned coinbase transactions received.\n" - " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" - " \"vout\" : n, (numeric) the vout value\n" - " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" - " 'send' category of transactions.\n" - " \"abandoned\" : xxx (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" - " 'send' category of transactions.\n" - " }\n" - " ,...\n" - " ],\n" - " \"hex\" : \"data\" (string) Raw data for transaction\n" - " \"decoded\" : transaction (json object) Optional, the decoded transaction (only present when `verbose` is passed), equivalent to the\n" - " RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed.\n" - "}\n" + RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>( + { + {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT}, + {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n" + "'send' category of transactions."}, + }, + TransactionDescriptionString()), + { + {RPCResult::Type::ARR, "details", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."}, + {RPCResult::Type::STR, "address", "The bitcoin address involved in the transaction."}, + {RPCResult::Type::STR, "category", "The transaction category.\n" + "\"send\" Transactions sent.\n" + "\"receive\" Non-coinbase transactions received.\n" + "\"generate\" Coinbase transactions received with more than 100 confirmations.\n" + "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" + "\"orphan\" Orphaned coinbase transactions received."}, + {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT}, + {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"}, + {RPCResult::Type::NUM, "vout", "the vout value"}, + {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + "'send' category of transactions."}, + {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + "'send' category of transactions."}, + }}, + }}, + {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"}, + {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)", + { + {RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."}, + }}, + }) }, RPCExamples{ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") @@ -2147,7 +2167,7 @@ static UniValue lockunspent(const JSONRPCRequest& request) }, }, RPCResult{ - "true|false (boolean) Whether the command was successful or not\n" + RPCResult::Type::BOOL, "", "Whether the command was successful or not" }, RPCExamples{ "\nList the unspent transactions\n" @@ -2257,13 +2277,14 @@ static UniValue listlockunspent(const JSONRPCRequest& request) "See the lockunspent call to lock and unlock transactions for spending.\n", {}, RPCResult{ - "[\n" - " {\n" - " \"txid\" : \"transactionid\", (string) The transaction id locked\n" - " \"vout\" : n (numeric) The vout value\n" - " }\n" - " ,...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"}, + {RPCResult::Type::NUM, "vout", "The vout value"}, + }}, + } }, RPCExamples{ "\nList the unspent transactions\n" @@ -2313,7 +2334,7 @@ static UniValue settxfee(const JSONRPCRequest& request) {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kB"}, }, RPCResult{ - "true|false (boolean) Returns true if successful\n" + RPCResult::Type::BOOL, "", "Returns true if successful" }, RPCExamples{ HelpExampleCli("settxfee", "0.00001") @@ -2351,19 +2372,23 @@ static UniValue getbalances(const JSONRPCRequest& request) "Returns an object with all balances in " + CURRENCY_UNIT + ".\n", {}, RPCResult{ - "{\n" - " \"mine\" : { (json object) balances from outputs that the wallet can sign\n" - " \"trusted\" : xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n" - " \"untrusted_pending\" : xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n" - " \"immature\" : xxx (numeric) balance from immature coinbase outputs\n" - " \"used\" : xxx (numeric) (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)\n" - " },\n" - " \"watchonly\" : { (json object) watchonly balances (not present if wallet does not watch anything)\n" - " \"trusted\" : xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n" - " \"untrusted_pending\" : xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n" - " \"immature\" : xxx (numeric) balance from immature coinbase outputs\n" - " },\n" - "}\n"}, + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign", + { + {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"}, + {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"}, + {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"}, + {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"}, + }}, + {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)", + { + {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"}, + {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"}, + {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"}, + }}, + } + }, RPCExamples{ HelpExampleCli("getbalances", "") + HelpExampleRpc("getbalances", "")}, @@ -2417,27 +2442,29 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) "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" - " \"balance\" : xxxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.trusted\n" - " \"unconfirmed_balance\" : xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n" - " \"immature_balance\" : xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n" - " \"txcount\" : xxxxxxx, (numeric) the total number of transactions in the wallet\n" - " \"keypoololdest\" : xxxxxx, (numeric) the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\" : xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n" - " \"keypoolsize_hd_internal\" : xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" - " \"unlocked_until\" : ttt, (numeric) the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\" : x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"hdseedid\" : \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" - " \"private_keys_enabled\" : true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n" - " \"avoid_reuse\" : true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n" - " \"scanning\" : (json object) current scanning details, or false if no scan is in progress\n" - " {\n" - " \"duration\" : xxxx (numeric) elapsed seconds since scan start\n" - " \"progress\" : x.xxxx, (numeric) scanning progress percentage [0.0, 1.0]\n" - " }\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + { + {RPCResult::Type::STR, "walletname", "the wallet name"}, + {RPCResult::Type::NUM, "walletversion", "the wallet version"}, + {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"}, + {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"}, + {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"}, + {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"}, + {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"}, + {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"}, + {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"}, + {RPCResult::Type::NUM_TIME, "unlocked_until", "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked"}, + {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"}, + {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"}, + {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"}, + {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"}, + {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress", + { + {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"}, + {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"}, + }}, + }}, }, RPCExamples{ HelpExampleCli("getwalletinfo", "") @@ -2499,14 +2526,16 @@ static UniValue listwalletdir(const JSONRPCRequest& request) "Returns a list of wallets in the wallet directory.\n", {}, RPCResult{ - "{\n" - " \"wallets\" : [ (json array of objects)\n" - " {\n" - " \"name\" : \"name\" (string) The wallet name\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "wallets", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "name", "The wallet name"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("listwalletdir", "") @@ -2533,10 +2562,10 @@ static UniValue listwallets(const JSONRPCRequest& request) "For full information on the wallet, use \"getwalletinfo\"\n", {}, RPCResult{ - "[ (json array of strings)\n" - " \"walletname\" (string) the wallet name\n" - " ...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR, "walletname", "the wallet name"}, + } }, RPCExamples{ HelpExampleCli("listwallets", "") @@ -2569,10 +2598,11 @@ static UniValue loadwallet(const JSONRPCRequest& request) {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."}, }, 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" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."}, + {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."}, + } }, RPCExamples{ HelpExampleCli("loadwallet", "\"test.dat\"") @@ -2624,11 +2654,12 @@ static UniValue setwalletflag(const JSONRPCRequest& request) {"value", RPCArg::Type::BOOL, /* default */ "true", "The new state."}, }, RPCResult{ - "{\n" - " \"flag_name\" : string (string) The name of the flag that was modified\n" - " \"flag_state\" : bool (boolean) The new state of the flag\n" - " \"warnings\" : string (string) Any warnings associated with the change\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"}, + {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"}, + {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"}, + } }, RPCExamples{ HelpExampleCli("setwalletflag", "avoid_reuse") @@ -2684,10 +2715,11 @@ static UniValue createwallet(const JSONRPCRequest& request) {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."}, }, 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" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."}, + {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."}, + } }, RPCExamples{ HelpExampleCli("createwallet", "\"testwallet\"") @@ -2813,27 +2845,28 @@ static UniValue listunspent(const JSONRPCRequest& request) "query_options"}, }, RPCResult{ - "[ (array of json object)\n" - " {\n" - " \"txid\" : \"txid\", (string) the transaction id \n" - " \"vout\" : n, (numeric) the vout value\n" - " \"address\" : \"address\", (string) the bitcoin address\n" - " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n" - " \"scriptPubKey\" : \"key\", (string) the script key\n" - " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH\n" - " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n" - " \"spendable\" : xxx, (boolean) Whether we have the private keys to spend this output\n" - " \"solvable\" : xxx, (boolean) Whether we know how to spend this output, ignoring the lack of keys\n" - " \"reused\" : xxx, (boolean) (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)\n" - " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n" - " \"safe\" : xxx (boolean) Whether this output is considered safe to spend. Unconfirmed transactions\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "the transaction id"}, + {RPCResult::Type::NUM, "vout", "the vout value"}, + {RPCResult::Type::STR, "address", "the bitcoin address"}, + {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"}, + {RPCResult::Type::STR, "scriptPubKey", "the script key"}, + {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations"}, + {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"}, + {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"}, + {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"}, + {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"}, + {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"}, + {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"}, + {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions" " from outside keys and unconfirmed replacement transactions are considered unsafe\n" - " and are not eligible for spending by fundrawtransaction and sendtoaddress.\n" - " }\n" - " ,...\n" - "]\n" + "and are not eligible for spending by fundrawtransaction and sendtoaddress."}, + }}, + } }, RPCExamples{ HelpExampleCli("listunspent", "") @@ -3171,11 +3204,12 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request) }, }, 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" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"}, + {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"}, + {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"}, + } }, RPCExamples{ "\nCreate a transaction with no inputs\n" @@ -3250,20 +3284,22 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) " \"SINGLE|ANYONECANPAY\""}, }, 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" - " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" - " {\n" - " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" - " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" - " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" - " \"sequence\" : n, (numeric) Script sequence number\n" - " \"error\" : \"text\" (string) Verification or signing error related to the input\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"}, + {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"}, + {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"}, + {RPCResult::Type::NUM, "sequence", "Script sequence number"}, + {RPCResult::Type::STR, "error", "Verification or signing error related to the input"}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") @@ -3360,13 +3396,16 @@ static UniValue bumpfee(const JSONRPCRequest& request) "options"}, }, RPCResult{ - "{\n" - " \"psbt\" : \"psbt\", (string) The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled.\n" - " \"txid\" : \"value\", (string) The id of the new transaction. Only returned when wallet private keys are enabled.\n" - " \"origfee\" : n, (numeric) The fee of the replaced transaction.\n" - " \"fee\" : n, (numeric) The fee of the new transaction.\n" - " \"errors\" : [ str... ] (json array of strings) Errors encountered during processing (may be empty).\n" - "}\n" + RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."}, + {RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."}, + {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."}, + {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."}, + {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).", + { + {RPCResult::Type::STR, "", ""}, + }}, + } }, RPCExamples{ "\nBump the fee, get the new transaction\'s txid\n" + @@ -3521,10 +3560,11 @@ UniValue rescanblockchain(const JSONRPCRequest& request) {"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."}, }, RPCResult{ - "{\n" - " \"start_height\" (numeric) The block height where the rescan started (the requested height or 0)\n" - " \"stop_height\" (numeric) The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background.\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "start_height", "The block height where the rescan started (the requested height or 0)"}, + {RPCResult::Type::NUM, "stop_height", "The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background."}, + } }, RPCExamples{ HelpExampleCli("rescanblockchain", "100000 120000") @@ -3729,49 +3769,52 @@ UniValue getaddressinfo(const JSONRPCRequest& request) {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."}, }, RPCResult{ - "{\n" - " \"address\" : \"address\", (string) The bitcoin address validated.\n" - " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address.\n" - " \"ismine\" : true|false, (boolean) If the address is yours.\n" - " \"iswatchonly\" : true|false, (boolean) If the address is watchonly.\n" - " \"solvable\" : true|false, (boolean) If we know how to spend coins sent to this address, ignoring the possible lack of private keys.\n" - " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable).\n" - " \"isscript\" : true|false, (boolean) If the key is a script.\n" - " \"ischange\" : true|false, (boolean) If the address was used for change output.\n" - " \"iswitness\" : true|false, (boolean) If the address is a witness address.\n" - " \"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" - " \"script\" : \"type\" (string, optional) The output script type. Only if isscript is true and the redeemscript is known. Possible\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The bitcoin address validated."}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."}, + {RPCResult::Type::BOOL, "ismine", "If the address is yours."}, + {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."}, + {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."}, + {RPCResult::Type::STR, "desc", /* optional */ true, "A descriptor for spending coins sent to this address (only when solvable)."}, + {RPCResult::Type::BOOL, "isscript", "If the key is a script."}, + {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."}, + {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."}, + {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."}, + {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."}, + {RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n" " types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n" - " witness_v0_scripthash, witness_unknown.\n" - " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address.\n" - " \"pubkeys\" (array, optional) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n" - " [\n" - " \"pubkey\" (string)\n" - " ,...\n" - " ]\n" - " \"sigsrequired\" : xxxxx (numeric, optional) The number of signatures required to spend multisig output (only if script is multisig).\n" - " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n" - " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. Includes all\n" + "witness_v0_scripthash, witness_unknown."}, + {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."}, + {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).", + { + {RPCResult::Type::STR, "pubkey", ""}, + }}, + {RPCResult::Type::NUM, "sigsrequired", /* optional */ true, "The number of signatures required to spend multisig output (only if script is multisig)."}, + {RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."}, + {RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.", + { + {RPCResult::Type::ELISION, "", "Includes all\n" " getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n" - " hdseedid) and relation to the wallet (ismine, iswatchonly).\n" - " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed.\n" - " \"label\" : \"label\" (string) DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below.\n" - " \"timestamp\" : timestamp, (number, optional) The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n" - " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n" - " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n" - " \"labels\" (json array) Array of labels associated with the address. Currently limited to one label but returned\n" - " as an array to keep the API stable if multiple labels are enabled in the future.\n" - " [\n" - " \"label name\" (string) The label name. Defaults to \"\".\n" - " DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`:\n" - " {\n" - " \"name\" : \"label name\" (string) The label name. Defaults to \"\".\n" - " \"purpose\" : \"purpose\" (string) The purpose of the associated address (send or receive).\n" - " }\n" - " ]\n" - "}\n" + "hdseedid) and relation to the wallet (ismine, iswatchonly)."}, + }}, + {RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."}, + {RPCResult::Type::STR, "label", "DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below."}, + {RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."}, + {RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."}, + {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."}, + {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /* optional */ true, "The fingerprint of the master key."}, + {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n" + "as an array to keep the API stable if multiple labels are enabled in the future.", + { + {RPCResult::Type::STR, "label name", "The label name. Defaults to \"\"."}, + {RPCResult::Type::OBJ, "", "label data, DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`", + { + {RPCResult::Type::STR, "name", "The label name. Defaults to \"\"."}, + {RPCResult::Type::STR, "purpose", "The purpose of the associated address (send or receive)."}, + }}, + }}, + } }, RPCExamples{ HelpExampleCli("getaddressinfo", EXAMPLE_ADDRESS) + @@ -3868,11 +3911,13 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request) {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."}, }, 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" + RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys", + { + {RPCResult::Type::OBJ, "address", "json object with information about address", + { + {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"}, + }}, + } }, RPCExamples{ HelpExampleCli("getaddressesbylabel", "\"tabby\"") @@ -3925,10 +3970,10 @@ static UniValue listlabels(const JSONRPCRequest& request) {"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."}, }, RPCResult{ - "[ (json array of string)\n" - " \"label\", (string) Label name\n" - " ...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR, "label", "Label name"}, + } }, RPCExamples{ "\nList all labels\n" @@ -4069,10 +4114,11 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"}, }, RPCResult{ - "{ (json object)\n" - " \"psbt\" : \"str\", (string) The base64-encoded partially signed transaction\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - "}\n" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + } }, RPCExamples{ HelpExampleCli("walletprocesspsbt", "\"psbt\"") @@ -4179,11 +4225,12 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"}, }, 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" + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"}, + {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"}, + {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"}, + } }, RPCExamples{ "\nCreate a transaction with no inputs\n" diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp index 9c9b27a413..0fbefb6023 100644 --- a/src/zmq/zmqrpc.cpp +++ b/src/zmq/zmqrpc.cpp @@ -19,14 +19,15 @@ UniValue getzmqnotifications(const JSONRPCRequest& request) "\nReturns information about the active ZeroMQ notifications.\n", {}, RPCResult{ - "[\n" - " { (json object)\n" - " \"type\" : \"pubhashtx\", (string) Type of notification\n" - " \"address\" : \"...\", (string) Address of the publisher\n" - " \"hwm\" : n (numeric) Outbound message high water mark\n" - " },\n" - " ...\n" - "]\n" + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "type", "Type of notification"}, + {RPCResult::Type::STR, "address", "Address of the publisher"}, + {RPCResult::Type::NUM, "hwm", "Outbound message high water mark"}, + }}, + } }, RPCExamples{ HelpExampleCli("getzmqnotifications", "") diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py new file mode 100755 index 0000000000..2c6553fbe2 --- /dev/null +++ b/test/functional/feature_asmap.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test asmap config argument for ASN-based IP bucketing. + +Verify node behaviour and debug log when launching bitcoind in these cases: + +1. `bitcoind` with no -asmap arg, using /16 prefix for IP bucketing + +2. `bitcoind -asmap=<absolute path>`, using the unit test skeleton asmap + +3. `bitcoind -asmap=<relative path>`, using the unit test skeleton asmap + +4. `bitcoind -asmap/-asmap=` with no file specified, using the default asmap + +5. `bitcoind -asmap` with no file specified and a missing default asmap file + +6. `bitcoind -asmap` with an empty (unparsable) default asmap file + +The tests are order-independent. + +""" +import os +import shutil + +from test_framework.test_framework import BitcoinTestFramework + +DEFAULT_ASMAP_FILENAME = 'ip_asn.map' # defined in src/init.cpp +ASMAP = '../../src/test/data/asmap.raw' # path to unit test skeleton asmap +VERSION = 'fec61fa21a9f46f3b17bdcd660d7f4cd90b966aad3aec593c99b35f0aca15853' + +def expected_messages(filename): + return ['Opened asmap file "{}" (59 bytes) from disk'.format(filename), + 'Using asmap version {} for IP bucketing'.format(VERSION)] + +class AsmapTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def test_without_asmap_arg(self): + self.log.info('Test bitcoind with no -asmap arg passed') + self.stop_node(0) + with self.node.assert_debug_log(['Using /16 prefix for IP bucketing']): + self.start_node(0) + + def test_asmap_with_absolute_path(self): + self.log.info('Test bitcoind -asmap=<absolute path>') + self.stop_node(0) + filename = os.path.join(self.datadir, 'my-map-file.map') + shutil.copyfile(self.asmap_raw, filename) + with self.node.assert_debug_log(expected_messages(filename)): + self.start_node(0, ['-asmap={}'.format(filename)]) + os.remove(filename) + + def test_asmap_with_relative_path(self): + self.log.info('Test bitcoind -asmap=<relative path>') + self.stop_node(0) + name = 'ASN_map' + filename = os.path.join(self.datadir, name) + shutil.copyfile(self.asmap_raw, filename) + with self.node.assert_debug_log(expected_messages(filename)): + self.start_node(0, ['-asmap={}'.format(name)]) + os.remove(filename) + + def test_default_asmap(self): + shutil.copyfile(self.asmap_raw, self.default_asmap) + for arg in ['-asmap', '-asmap=']: + self.log.info('Test bitcoind {} (using default map file)'.format(arg)) + self.stop_node(0) + with self.node.assert_debug_log(expected_messages(self.default_asmap)): + self.start_node(0, [arg]) + os.remove(self.default_asmap) + + def test_default_asmap_with_missing_file(self): + self.log.info('Test bitcoind -asmap with missing default map file') + self.stop_node(0) + msg = "Error: Could not find asmap file \"{}\"".format(self.default_asmap) + self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) + + def test_empty_asmap(self): + self.log.info('Test bitcoind -asmap with empty map file') + self.stop_node(0) + with open(self.default_asmap, "w", encoding="utf-8") as f: + f.write("") + msg = "Error: Could not parse asmap file \"{}\"".format(self.default_asmap) + self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg) + os.remove(self.default_asmap) + + def run_test(self): + self.node = self.nodes[0] + self.datadir = os.path.join(self.node.datadir, self.chain) + self.default_asmap = os.path.join(self.datadir, DEFAULT_ASMAP_FILENAME) + self.asmap_raw = os.path.join(os.path.dirname(os.path.realpath(__file__)), ASMAP) + + self.test_without_asmap_arg() + self.test_asmap_with_absolute_path() + self.test_asmap_with_relative_path() + self.test_default_asmap() + self.test_default_asmap_with_missing_file() + self.test_empty_asmap() + + +if __name__ == '__main__': + AsmapTest().main() diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py index 7a6e3df702..0db74432e2 100755 --- a/test/functional/feature_backwards_compatibility.py +++ b/test/functional/feature_backwards_compatibility.py @@ -113,7 +113,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): # Create another conflicting transaction using RBF tx3_id = self.nodes[1].sendtoaddress(return_address, 1) tx4_id = self.nodes[1].bumpfee(tx3_id)["txid"] - # Abondon transaction, but don't confirm + # Abandon transaction, but don't confirm self.nodes[1].abandontransaction(tx3_id) # w1_v19: regular wallet, created with v0.19 diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 95905f477b..38bf2faf89 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -1261,7 +1261,7 @@ class FullBlockTest(BitcoinTestFramework): self.save_spendable_output() spend = self.get_spendable_output() - self.send_blocks(blocks, True, timeout=960) + self.send_blocks(blocks, True, timeout=1920) chain1_tip = i # now create alt chain of same length @@ -1273,14 +1273,14 @@ class FullBlockTest(BitcoinTestFramework): # extend alt chain to trigger re-org block = self.next_block("alt" + str(chain1_tip + 1), version=4) - self.send_blocks([block], True, timeout=960) + self.send_blocks([block], True, timeout=1920) # ... and re-org back to the first chain self.move_tip(chain1_tip) block = self.next_block(chain1_tip + 1, version=4) self.send_blocks([block], False, force_send=True) block = self.next_block(chain1_tip + 2, version=4) - self.send_blocks([block], True, timeout=960) + self.send_blocks([block], True, timeout=1920) self.log.info("Reject a block with an invalid block header version") b_v1 = self.next_block('b_v1', version=1) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index ad3d5a486b..3a63377545 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -437,6 +437,10 @@ class PSBTTest(BitcoinTestFramework): assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Input 0 has invalid value') + self.log.info("PSBT with signed, but not finalized, inputs should have Finalizer as next") + analysis = self.nodes[0].analyzepsbt('cHNidP8BAHECAAAAAZYezcxdnbXoQCmrD79t/LzDgtUo9ERqixk8wgioAobrAAAAAAD9////AlDDAAAAAAAAFgAUy/UxxZuzZswcmFnN/E9DGSiHLUsuGPUFAAAAABYAFLsH5o0R38wXx+X2cCosTMCZnQ4baAAAAAABAR8A4fUFAAAAABYAFOBI2h5thf3+Lflb2LGCsVSZwsltIgIC/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnJHMEQCIGx7zKcMIGr7cEES9BR4Kdt/pzPTK3fKWcGyCJXb7MVnAiALOBgqlMH4GbC1HDh/HmylmO54fyEy4lKde7/BT/PWxwEBAwQBAAAAIgYC/i4dtVARCRWtROG0HHoGcaVklzJUcwo5homgGkSNAnIYDwVpQ1QAAIABAACAAAAAgAAAAAAAAAAAAAAiAgL+CIiB59NSCssOJRGiMYQK1chahgAaaJpIXE41Cyir+xgPBWlDVAAAgAEAAIAAAACAAQAAAAAAAAAA') + assert_equal(analysis['next'], 'finalizer') + analysis = self.nodes[0].analyzepsbt('cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgCAgWrj0AcAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAR8A8gUqAQAAABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA') assert_equal(analysis['next'], 'creator') assert_equal(analysis['error'], 'PSBT is not valid. Output amount invalid') diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 06d939afb7..2036d20852 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -206,6 +206,7 @@ BASE_SCRIPTS = [ 'p2p_dos_header_tree.py', 'p2p_unrequested_blocks.py', 'feature_includeconf.py', + 'feature_asmap.py', 'rpc_deriveaddresses.py', 'rpc_deriveaddresses.py --usecli', 'rpc_scantxoutset.py', diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index 1a74c67503..5df2789638 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -25,8 +25,10 @@ FUZZERS_MISSING_CORPORA = [ "decode_tx", "fee_rate_deserialize", "flat_file_pos_deserialize", + "float", "hex", "integer", + "key", "key_origin_info_deserialize", "merkle_block_deserialize", "out_point_deserialize", @@ -134,7 +136,7 @@ def main(): os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', test_list_selection[0]), '-help=1', ], - timeout=10, + timeout=20, check=True, stderr=subprocess.PIPE, universal_newlines=True, diff --git a/test/lint/extended-lint-cppcheck.sh b/test/lint/extended-lint-cppcheck.sh index 4d6c70660f..ae18d74ebf 100755 --- a/test/lint/extended-lint-cppcheck.sh +++ b/test/lint/extended-lint-cppcheck.sh @@ -66,7 +66,7 @@ function join_array { ENABLED_CHECKS_REGEXP=$(join_array "|" "${ENABLED_CHECKS[@]}") IGNORED_WARNINGS_REGEXP=$(join_array "|" "${IGNORED_WARNINGS[@]}") WARNINGS=$(git ls-files -- "*.cpp" "*.h" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" | \ - xargs cppcheck --enable=all -j "$(getconf _NPROCESSORS_ONLN)" --language=c++ --std=c++11 --template=gcc -D__cplusplus -DCLIENT_VERSION_BUILD -DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR -DDEBUG -DHAVE_WORKING_BOOST_SLEEP_FOR -I src/ -q 2>&1 | sort -u | \ + xargs cppcheck --enable=all -j "$(getconf _NPROCESSORS_ONLN)" --language=c++ --std=c++11 --template=gcc -D__cplusplus -DCLIENT_VERSION_BUILD -DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR -DDEBUG -I src/ -q 2>&1 | sort -u | \ grep -E "${ENABLED_CHECKS_REGEXP}" | \ grep -vE "${IGNORED_WARNINGS_REGEXP}") if [[ ${WARNINGS} != "" ]]; then diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index ced2fd2bb6..1cece6a525 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -53,7 +53,6 @@ EXPECTED_BOOST_INCLUDES=( boost/algorithm/string/classification.hpp boost/algorithm/string/replace.hpp boost/algorithm/string/split.hpp - boost/chrono/chrono.hpp boost/date_time/posix_time/posix_time.hpp boost/filesystem.hpp boost/filesystem/fstream.hpp diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 35e58c2df6..0cb38b6fdb 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -22,6 +22,7 @@ KNOWN_VIOLATIONS=( "src/test/blockchain_tests.cpp.*std::to_string" "src/test/dbwrapper_tests.cpp:.*snprintf" "src/test/denialofservice_tests.cpp.*std::to_string" + "src/test/fuzz/locale.cpp" "src/test/fuzz/parse_numbers.cpp:.*atoi" "src/test/key_tests.cpp.*std::to_string" "src/test/net_tests.cpp.*std::to_string" diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh index 63624e3ae0..f59b2c9945 100755 --- a/test/lint/lint-shell.sh +++ b/test/lint/lint-shell.sh @@ -41,7 +41,7 @@ if ! shellcheck "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE 'src/(leveldb|sec fi if ! command -v yq > /dev/null; then - echo "Skipping Gitian desriptor scripts checking since yq is not installed." + echo "Skipping Gitian descriptor scripts checking since yq is not installed." exit $EXIT_CODE fi diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt index 576ae94098..a7a97eb41f 100644 --- a/test/lint/lint-spelling.ignore-words.txt +++ b/test/lint/lint-spelling.ignore-words.txt @@ -12,3 +12,5 @@ keyserver homogenous setban hist +ser +unselect diff --git a/test/lint/lint-submodule.sh b/test/lint/lint-submodule.sh new file mode 100755 index 0000000000..d9aa021df7 --- /dev/null +++ b/test/lint/lint-submodule.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# This script checks for git modules +export LC_ALL=C +EXIT_CODE=0 + +CMD=$(git submodule status --recursive) +if test -n "$CMD"; +then + echo These submodules were found, delete them: + echo "$CMD" + EXIT_CODE=1 +fi + +exit $EXIT_CODE + |