diff options
Diffstat (limited to 'src')
107 files changed, 2132 insertions, 1084 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8f37e055ff..9903c2e9b3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -182,7 +182,6 @@ BITCOIN_CORE_H = \ node/ui_interface.h \ node/utxo_snapshot.h \ noui.h \ - optional.h \ outputtype.h \ policy/feerate.h \ policy/fees.h \ @@ -553,7 +552,6 @@ libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ - compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 625d825259..e00f17a83f 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -297,7 +297,10 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/transaction.cpp \ test/fuzz/tx_in.cpp \ test/fuzz/tx_out.cpp \ - test/fuzz/txrequest.cpp + test/fuzz/tx_pool.cpp \ + test/fuzz/txrequest.cpp \ + test/fuzz/validation_load_mempool.cpp \ + test/fuzz/versionbits.cpp endif # ENABLE_FUZZ_BINARY nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/bech32.cpp b/src/bech32.cpp index 1e0471f110..288b14e023 100644 --- a/src/bech32.cpp +++ b/src/bech32.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Pieter Wuille +// Copyright (c) 2017, 2021 Pieter Wuille // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,15 +7,18 @@ #include <assert.h> +namespace bech32 +{ + namespace { typedef std::vector<uint8_t> data; -/** The Bech32 character set for encoding. */ +/** The Bech32 and Bech32m character set for encoding. */ const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; -/** The Bech32 character set for decoding. */ +/** The Bech32 and Bech32m character set for decoding. */ const int8_t CHARSET_REV[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -27,6 +30,12 @@ const int8_t CHARSET_REV[128] = { 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 }; +/* Determine the final constant to use for the specified encoding. */ +uint32_t EncodingConstant(Encoding encoding) { + assert(encoding == Encoding::BECH32 || encoding == Encoding::BECH32M); + return encoding == Encoding::BECH32 ? 1 : 0x2bc830a3; +} + /** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher * bits correspond to earlier values. */ @@ -111,21 +120,24 @@ data ExpandHRP(const std::string& hrp) } /** Verify a checksum. */ -bool VerifyChecksum(const std::string& hrp, const data& values) +Encoding VerifyChecksum(const std::string& hrp, const data& values) { // PolyMod computes what value to xor into the final values to make the checksum 0. However, // if we required that the checksum was 0, it would be the case that appending a 0 to a valid // list of values would result in a new valid list. For that reason, Bech32 requires the - // resulting checksum to be 1 instead. - return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; + // resulting checksum to be 1 instead. In Bech32m, this constant was amended. + const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values)); + if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32; + if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M; + return Encoding::INVALID; } /** Create a checksum. */ -data CreateChecksum(const std::string& hrp, const data& values) +data CreateChecksum(Encoding encoding, const std::string& hrp, const data& values) { data enc = Cat(ExpandHRP(hrp), values); enc.resize(enc.size() + 6); // Append 6 zeroes - uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + uint32_t mod = PolyMod(enc) ^ EncodingConstant(encoding); // Determine what to XOR into those 6 zeroes. data ret(6); for (size_t i = 0; i < 6; ++i) { // Convert the 5-bit groups in mod to checksum values. @@ -136,16 +148,13 @@ data CreateChecksum(const std::string& hrp, const data& values) } // namespace -namespace bech32 -{ - -/** Encode a Bech32 string. */ -std::string Encode(const std::string& hrp, const data& values) { - // First ensure that the HRP is all lowercase. BIP-173 requires an encoder - // to return a lowercase Bech32 string, but if given an uppercase HRP, the +/** Encode a Bech32 or Bech32m string. */ +std::string Encode(Encoding encoding, const std::string& hrp, const data& values) { + // First ensure that the HRP is all lowercase. BIP-173 and BIP350 require an encoder + // to return a lowercase Bech32/Bech32m string, but if given an uppercase HRP, the // result will always be invalid. for (const char& c : hrp) assert(c < 'A' || c > 'Z'); - data checksum = CreateChecksum(hrp, values); + data checksum = CreateChecksum(encoding, hrp, values); data combined = Cat(values, checksum); std::string ret = hrp + '1'; ret.reserve(ret.size() + combined.size()); @@ -155,8 +164,8 @@ std::string Encode(const std::string& hrp, const data& values) { return ret; } -/** Decode a Bech32 string. */ -std::pair<std::string, data> Decode(const std::string& str) { +/** Decode a Bech32 or Bech32m string. */ +DecodeResult Decode(const std::string& str) { bool lower = false, upper = false; for (size_t i = 0; i < str.size(); ++i) { unsigned char c = str[i]; @@ -183,10 +192,9 @@ std::pair<std::string, data> Decode(const std::string& str) { for (size_t i = 0; i < pos; ++i) { hrp += LowerCase(str[i]); } - if (!VerifyChecksum(hrp, values)) { - return {}; - } - return {hrp, data(values.begin(), values.end() - 6)}; + Encoding result = VerifyChecksum(hrp, values); + if (result == Encoding::INVALID) return {}; + return {result, std::move(hrp), data(values.begin(), values.end() - 6)}; } } // namespace bech32 diff --git a/src/bech32.h b/src/bech32.h index fb39cd352b..e9450ccc2b 100644 --- a/src/bech32.h +++ b/src/bech32.h @@ -1,13 +1,14 @@ -// Copyright (c) 2017 Pieter Wuille +// Copyright (c) 2017, 2021 Pieter Wuille // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -// Bech32 is a string encoding format used in newer address types. -// The output consists of a human-readable part (alphanumeric), a -// separator character (1), and a base32 data section, the last -// 6 characters of which are a checksum. +// Bech32 and Bech32m are string encoding formats used in newer +// address types. The outputs consist of a human-readable part +// (alphanumeric), a separator character (1), and a base32 data +// section, the last 6 characters of which are a checksum. The +// module is namespaced under bech32 for historical reasons. // -// For more information, see BIP 173. +// For more information, see BIP 173 and BIP 350. #ifndef BITCOIN_BECH32_H #define BITCOIN_BECH32_H @@ -19,11 +20,29 @@ namespace bech32 { -/** Encode a Bech32 string. If hrp contains uppercase characters, this will cause an assertion error. */ -std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values); +enum class Encoding { + INVALID, //!< Failed decoding -/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ -std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str); + BECH32, //!< Bech32 encoding as defined in BIP173 + BECH32M, //!< Bech32m encoding as defined in BIP350 +}; + +/** Encode a Bech32 or Bech32m string. If hrp contains uppercase characters, this will cause an + * assertion error. Encoding must be one of BECH32 or BECH32M. */ +std::string Encode(Encoding encoding, const std::string& hrp, const std::vector<uint8_t>& values); + +struct DecodeResult +{ + Encoding encoding; //!< What encoding was detected in the result; Encoding::INVALID if failed. + std::string hrp; //!< The human readable part + std::vector<uint8_t> data; //!< The payload (excluding checksum) + + DecodeResult() : encoding(Encoding::INVALID) {} + DecodeResult(Encoding enc, std::string&& h, std::vector<uint8_t>&& d) : encoding(enc), hrp(std::move(h)), data(std::move(d)) {} +}; + +/** Decode a Bech32 or Bech32m string. */ +DecodeResult Decode(const std::string& str); } // namespace bech32 diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp index c74d8d51b3..8e10862a37 100644 --- a/src/bench/bech32.cpp +++ b/src/bench/bech32.cpp @@ -19,7 +19,7 @@ static void Bech32Encode(benchmark::Bench& bench) tmp.reserve(1 + 32 * 8 / 5); ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end()); bench.batch(v.size()).unit("byte").run([&] { - bech32::Encode("bc", tmp); + bech32::Encode(bech32::Encoding::BECH32, "bc", tmp); }); } diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 376e70ffba..80acdec044 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -49,7 +49,10 @@ static void CoinSelection(benchmark::Bench& bench) } const CoinEligibilityFilter filter_standard(1, 6, 0); - const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0, false); + const CoinSelectionParams coin_selection_params(/* use_bnb= */ true, /* change_output_size= */ 34, + /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), + /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), + /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); bench.run([&] { std::set<CInputCoin> setCoinsRet; CAmount nValueRet; diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp index c96ef209e3..d7cc167885 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -5,13 +5,14 @@ #include <bench/bench.h> #include <interfaces/chain.h> #include <node/context.h> -#include <optional.h> #include <test/util/mining.h> #include <test/util/setup_common.h> #include <test/util/wallet.h> #include <validationinterface.h> #include <wallet/wallet.h> +#include <optional> + static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_watchonly, const bool add_mine) { const auto test_setup = MakeNoLogFileContext<const TestingSetup>(); @@ -26,7 +27,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b } auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}}); - const Optional<std::string> address_mine{add_mine ? Optional<std::string>{getnewaddress(wallet)} : nullopt}; + const std::optional<std::string> address_mine{add_mine ? std::optional<std::string>{getnewaddress(wallet)} : std::nullopt}; if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY); for (int i = 0; i < 100; ++i) { diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index dc4b142f83..4f3d7a4ffe 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -9,7 +9,6 @@ #include <chainparamsbase.h> #include <clientversion.h> -#include <optional.h> #include <rpc/client.h> #include <rpc/mining.h> #include <rpc/protocol.h> @@ -24,6 +23,7 @@ #include <cmath> #include <functional> #include <memory> +#include <optional> #include <stdio.h> #include <string> #include <tuple> @@ -611,16 +611,16 @@ public: } }; -static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {}) +static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {}) { std::string host; // In preference order, we choose the following for the port: // 1. -rpcport // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) // 3. default port for chain - int port = BaseParams().RPCPort(); + uint16_t port{BaseParams().RPCPort()}; SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); - port = gArgs.GetArg("-rpcport", port); + port = static_cast<uint16_t>(gArgs.GetArg("-rpcport", port)); // Obtain event base raii_event_base base = obtain_event_base(); @@ -708,6 +708,8 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co } else { throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword"); } + } else if (response.status == HTTP_SERVICE_UNAVAILABLE) { + throw std::runtime_error(strprintf("Server response: %s", response.body)); } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) throw std::runtime_error(strprintf("server returned HTTP error %d", response.status)); else if (response.body.empty()) @@ -733,7 +735,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co * @returns the RPC response as a UniValue object. * @throws a CConnectionFailed std::runtime_error if connection failed or RPC server still in warmup. */ -static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {}) +static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {}) { UniValue response(UniValue::VOBJ); // Execute and handle connection failures with -rpcwait. @@ -817,7 +819,7 @@ static void GetWalletBalances(UniValue& result) */ static UniValue GetNewAddress() { - Optional<std::string> wallet_name{}; + std::optional<std::string> wallet_name{}; if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", ""); DefaultRequestHandler rh; return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name); @@ -922,7 +924,7 @@ static int CommandLineRPC(int argc, char *argv[]) } if (nRet == 0) { // Perform RPC call - Optional<std::string> wallet_name{}; + std::optional<std::string> wallet_name{}; if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", ""); const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 32f06aec2c..1b4ca3e9a8 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -25,6 +25,7 @@ #include <util/url.h> #include <functional> +#include <optional> const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = urlDecode; diff --git a/src/chainparams.h b/src/chainparams.h index 4d24dcdb7c..013f075be6 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -84,7 +84,7 @@ public: const Consensus::Params& GetConsensus() const { return consensus; } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } - int GetDefaultPort() const { return nDefaultPort; } + uint16_t GetDefaultPort() const { return nDefaultPort; } const CBlock& GenesisBlock() const { return genesis; } /** Default value for -checkmempool and -checkblockindex argument */ @@ -121,7 +121,7 @@ protected: Consensus::Params consensus; CMessageHeader::MessageStartChars pchMessageStart; - int nDefaultPort; + uint16_t nDefaultPort; uint64_t nPruneAfterHeight; uint64_t m_assumed_blockchain_size; uint64_t m_assumed_chain_state_size; diff --git a/src/coins.h b/src/coins.h index feb441fd6a..5a6f73652b 100644 --- a/src/coins.h +++ b/src/coins.h @@ -75,6 +75,9 @@ public: ::Unserialize(s, Using<TxOutCompression>(out)); } + /** Either this coin never existed (see e.g. coinEmpty in coins.cpp), or it + * did exist and has been spent. + */ bool IsSpent() const { return out.IsNull(); } diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index 8a51f310f7..ff581d4a9e 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -9,13 +9,6 @@ #include <cstddef> #include <cstdint> -// Prior to GLIBC_2.14, memcpy was aliased to memmove. -extern "C" void* memmove(void* a, const void* b, size_t c); -extern "C" void* memcpy(void* a, const void* b, size_t c) -{ - return memmove(a, b, c); -} - #if defined(__i386__) || defined(__arm__) extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp deleted file mode 100644 index 06d0dd6fba..0000000000 --- a/src/compat/glibc_sanity.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2009-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif - -#include <cstddef> - -extern "C" void* memcpy(void* a, const void* b, size_t c); -void* memcpy_int(void* a, const void* b, size_t c) -{ - return memcpy(a, b, c); -} - -namespace -{ -// trigger: Use the memcpy_int wrapper which calls our internal memcpy. -// A direct call to memcpy may be optimized away by the compiler. -// test: Fill an array with a sequence of integers. memcpy to a new empty array. -// Verify that the arrays are equal. Use an odd size to decrease the odds of -// the call being optimized away. -template <unsigned int T> -bool sanity_test_memcpy() -{ - unsigned int memcpy_test[T]; - unsigned int memcpy_verify[T] = {}; - for (unsigned int i = 0; i != T; ++i) - memcpy_test[i] = i; - - memcpy_int(memcpy_verify, memcpy_test, sizeof(memcpy_test)); - - for (unsigned int i = 0; i != T; ++i) { - if (memcpy_verify[i] != i) - return false; - } - return true; -} -} // namespace - -bool glibc_sanity_test() -{ - return sanity_test_memcpy<1025>(); -} diff --git a/src/compat/sanity.h b/src/compat/sanity.h index 909c4f6da8..8efa416102 100644 --- a/src/compat/sanity.h +++ b/src/compat/sanity.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_COMPAT_SANITY_H #define BITCOIN_COMPAT_SANITY_H -bool glibc_sanity_test(); bool glibcxx_sanity_test(); #endif // BITCOIN_COMPAT_SANITY_H diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0a8e58ab67..12395f5b24 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -262,7 +262,7 @@ 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_SERVER_ERROR, "Work queue depth exceeded"); + item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded"); } } else { hreq->WriteReply(HTTP_NOT_FOUND); @@ -290,8 +290,8 @@ static bool ThreadHTTP(struct event_base* base) /** Bind HTTP server to specified addresses */ static bool HTTPBindAddresses(struct evhttp* http) { - int http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); - std::vector<std::pair<std::string, uint16_t> > endpoints; + uint16_t http_port{static_cast<uint16_t>(gArgs.GetArg("-rpcport", BaseParams().RPCPort()))}; + std::vector<std::pair<std::string, uint16_t>> endpoints; // Determine what addresses to bind to if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs @@ -305,7 +305,7 @@ static bool HTTPBindAddresses(struct evhttp* http) } } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) { - int port = http_port; + uint16_t port{http_port}; std::string host; SplitHostPort(strRPCBind, port, host); endpoints.push_back(std::make_pair(host, port)); diff --git a/src/i2p.cpp b/src/i2p.cpp index 42270deaeb..d16c620d88 100644 --- a/src/i2p.cpp +++ b/src/i2p.cpp @@ -153,7 +153,7 @@ bool Session::Accept(Connection& conn) } const std::string& peer_dest = - conn.sock.RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt); + conn.sock.RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE); conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort()); @@ -252,7 +252,7 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock, // signaled. static constexpr auto recv_timeout = 3min; - reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt); + reply.full = sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt, MAX_MSG_SIZE); for (const auto& kv : spanparsing::Split(reply.full, ' ')) { const auto& pos = std::find(kv.begin(), kv.end(), '='); @@ -41,6 +41,14 @@ struct Connection { namespace sam { /** + * The maximum size of an incoming message from the I2P SAM proxy (in bytes). + * Used to avoid a runaway proxy from sending us an "unlimited" amount of data without a terminator. + * The longest known message is ~1400 bytes, so this is high enough not to be triggered during + * normal operation, yet low enough to avoid a malicious proxy from filling our memory. + */ +static constexpr size_t MAX_MSG_SIZE{65536}; + +/** * I2P SAM session. */ class Session diff --git a/src/init.cpp b/src/init.cpp index b91a94bc25..7d5420e3be 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -585,6 +585,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS); #else hidden_args.emplace_back("-daemon"); + hidden_args.emplace_back("-daemonwait"); #endif // Add the hidden options @@ -772,7 +773,7 @@ static bool InitSanityCheck() return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting.")); } - if (!glibc_sanity_test() || !glibcxx_sanity_test()) + if (!glibcxx_sanity_test()) return false; if (!Random_SanityCheck()) { diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index ebc5466173..3395741b1b 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -5,12 +5,12 @@ #ifndef BITCOIN_INTERFACES_CHAIN_H #define BITCOIN_INTERFACES_CHAIN_H -#include <optional.h> // For Optional and nullopt #include <primitives/transaction.h> // For CTransactionRef #include <util/settings.h> // For util::SettingsValue #include <functional> #include <memory> +#include <optional> #include <stddef.h> #include <stdint.h> #include <string> @@ -94,7 +94,7 @@ public: //! Get current chain height, not including genesis block (returns 0 if //! chain only contains genesis block, nullopt if chain does not contain //! any blocks) - virtual Optional<int> getHeight() = 0; + virtual std::optional<int> getHeight() = 0; //! Get block hash. Height must be valid or this function will abort. virtual uint256 getBlockHash(int height) = 0; @@ -109,7 +109,7 @@ public: //! Return height of the highest block on chain in common with the locator, //! which will either be the original block used to create the locator, //! or one of its ancestors. - virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0; + virtual std::optional<int> findLocatorFork(const CBlockLocator& locator) = 0; //! Check if transaction will be final given chain height current time. virtual bool checkFinalTx(const CTransaction& tx) = 0; @@ -154,7 +154,7 @@ public: //! Return true if data is available for all blocks in the specified range //! of blocks. This checks all blocks that are ancestors of block_hash in //! the height range from min_height to max_height, inclusive. - virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, Optional<int> max_height = {}) = 0; + virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, std::optional<int> max_height = {}) = 0; //! Check if transaction is RBF opt in. virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0; diff --git a/src/key_io.cpp b/src/key_io.cpp index e27673fd16..dbcbfa1f29 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -43,7 +43,7 @@ public: std::vector<unsigned char> data = {0}; data.reserve(33); ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end()); - return bech32::Encode(m_params.Bech32HRP(), data); + return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data); } std::string operator()(const WitnessV0ScriptHash& id) const @@ -51,7 +51,7 @@ public: std::vector<unsigned char> data = {0}; data.reserve(53); ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end()); - return bech32::Encode(m_params.Bech32HRP(), data); + return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data); } std::string operator()(const WitnessUnknown& id) const @@ -62,7 +62,7 @@ public: std::vector<unsigned char> data = {(unsigned char)id.version}; data.reserve(1 + (id.length * 8 + 4) / 5); ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length); - return bech32::Encode(m_params.Bech32HRP(), data); + return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data); } std::string operator()(const CNoDestination& no) const { return {}; } @@ -95,20 +95,26 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par error_str = "Invalid prefix for Base58-encoded address"; } data.clear(); - auto bech = bech32::Decode(str); - if (bech.second.size() > 0) { + const auto dec = bech32::Decode(str); + if ((dec.encoding == bech32::Encoding::BECH32 || dec.encoding == bech32::Encoding::BECH32M) && dec.data.size() > 0) { + // Bech32 decoding error_str = ""; - - if (bech.first != params.Bech32HRP()) { + if (dec.hrp != params.Bech32HRP()) { error_str = "Invalid prefix for Bech32 address"; return CNoDestination(); } - - // Bech32 decoding - int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) + int version = dec.data[0]; // The first 5 bit symbol is the witness version (0-16) + if (version == 0 && dec.encoding != bech32::Encoding::BECH32) { + error_str = "Version 0 witness address must use Bech32 checksum"; + return CNoDestination(); + } + if (version != 0 && dec.encoding != bech32::Encoding::BECH32M) { + error_str = "Version 1+ witness address must use Bech32m checksum"; + return CNoDestination(); + } // The rest of the symbols are converted witness program bytes. - data.reserve(((bech.second.size() - 1) * 5) / 8); - if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) { + data.reserve(((dec.data.size() - 1) * 5) / 8); + if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, dec.data.begin() + 1, dec.data.end())) { if (version == 0) { { WitnessV0KeyHash keyid; diff --git a/src/miner.cpp b/src/miner.cpp index fbaef0f224..fe7a54c052 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -96,9 +96,6 @@ void BlockAssembler::resetBlock() nFees = 0; } -Optional<int64_t> BlockAssembler::m_last_block_num_txs{nullopt}; -Optional<int64_t> BlockAssembler::m_last_block_weight{nullopt}; - std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn) { int64_t nTimeStart = GetTimeMicros(); diff --git a/src/miner.h b/src/miner.h index a67fec6dd8..023635814c 100644 --- a/src/miner.h +++ b/src/miner.h @@ -6,12 +6,12 @@ #ifndef BITCOIN_MINER_H #define BITCOIN_MINER_H -#include <optional.h> #include <primitives/block.h> #include <txmempool.h> #include <validation.h> #include <memory> +#include <optional> #include <stdint.h> #include <boost/multi_index_container.hpp> @@ -160,8 +160,8 @@ public: /** Construct a new block template with coinbase to scriptPubKeyIn */ std::unique_ptr<CBlockTemplate> CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn); - static Optional<int64_t> m_last_block_num_txs; - static Optional<int64_t> m_last_block_weight; + inline static std::optional<int64_t> m_last_block_num_txs{}; + inline static std::optional<int64_t> m_last_block_weight{}; private: // utility functions diff --git a/src/net.cpp b/src/net.cpp index 1e4a6a9aa7..3b1ebede98 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -18,7 +18,6 @@ #include <net_permissions.h> #include <netbase.h> #include <node/ui_interface.h> -#include <optional.h> #include <protocol.h> #include <random.h> #include <scheduler.h> @@ -39,6 +38,7 @@ #include <algorithm> #include <cstdint> #include <functional> +#include <optional> #include <unordered_map> #include <math.h> @@ -113,7 +113,7 @@ void CConnman::AddAddrFetch(const std::string& strDest) uint16_t GetListenPort() { - return (uint16_t)(gArgs.GetArg("-port", Params().GetDefaultPort())); + return static_cast<uint16_t>(gArgs.GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer @@ -193,7 +193,7 @@ bool IsPeerAddrLocalGood(CNode *pnode) IsReachable(addrLocal.GetNetwork()); } -Optional<CAddress> GetLocalAddrForPeer(CNode *pnode) +std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode) { CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); if (gArgs.GetBoolArg("-addrmantest", false)) { @@ -215,7 +215,7 @@ Optional<CAddress> GetLocalAddrForPeer(CNode *pnode) return addrLocal; } // Address is unroutable. Don't advertise. - return nullopt; + return std::nullopt; } // learn a new local address @@ -394,7 +394,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); // Resolve - const int default_port = Params().GetDefaultPort(); + const uint16_t default_port{Params().GetDefaultPort()}; if (pszDest) { std::vector<CService> resolved; if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { @@ -462,7 +462,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo return nullptr; } std::string host; - int port = default_port; + uint16_t port{default_port}; SplitHostPort(std::string(pszDest), port, host); bool proxyConnectionFailed; connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout, @@ -632,7 +632,7 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete) if (m_deserializer->Complete()) { // decompose a transport agnostic CNetMessage from the deserializer uint32_t out_err_raw_size{0}; - Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)}; + std::optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)}; if (!result) { // Message deserialization failed. Drop the message but don't disconnect the peer. // store the size of the corrupt message @@ -723,10 +723,10 @@ const uint256& V1TransportDeserializer::GetMessageHash() const return data_hash; } -Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size) +std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size) { // decompose a single CNetMessage from the TransportDeserializer - Optional<CNetMessage> msg(std::move(vRecv)); + std::optional<CNetMessage> msg(std::move(vRecv)); // store command string, time, and sizes msg->m_command = hdr.GetCommand(); @@ -747,12 +747,12 @@ Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::mic HexStr(hdr.pchChecksum), m_node_id); out_err_raw_size = msg->m_raw_message_size; - msg = nullopt; + msg = std::nullopt; } else if (!hdr.IsCommandValid()) { LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n", hdr.GetCommand(), msg->m_message_size, m_node_id); out_err_raw_size = msg->m_raw_message_size; - msg = nullopt; + msg.reset(); } // Always reset the network deserializer (prepare for the next message) @@ -879,7 +879,7 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, elements.erase(elements.end() - eraseSize, elements.end()); } -[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates) +[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates) { // Protect connections with certain characteristics @@ -918,7 +918,7 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, total_protect_size -= initial_size - vEvictionCandidates.size(); EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size); - if (vEvictionCandidates.empty()) return nullopt; + if (vEvictionCandidates.empty()) return std::nullopt; // If any remaining peers are preferred for eviction consider only them. // This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks) @@ -989,7 +989,7 @@ bool CConnman::AttemptToEvictConnection() vEvictionCandidates.push_back(candidate); } } - const Optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates)); + const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates)); if (!node_id_to_evict) { return false; } @@ -18,7 +18,6 @@ #include <net_permissions.h> #include <netaddress.h> #include <netbase.h> -#include <optional.h> #include <policy/feerate.h> #include <protocol.h> #include <random.h> @@ -35,6 +34,7 @@ #include <deque> #include <map> #include <memory> +#include <optional> #include <thread> #include <vector> @@ -200,7 +200,7 @@ enum bool IsPeerAddrLocalGood(CNode *pnode); /** Returns a local address that we should advertise to this peer */ -Optional<CAddress> GetLocalAddrForPeer(CNode *pnode); +std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode); /** * Mark a network as reachable or unreachable (no automatic connects to it) @@ -229,7 +229,7 @@ extern std::string strSubVersion; struct LocalServiceInfo { int nScore; - int nPort; + uint16_t nPort; }; extern RecursiveMutex cs_mapLocalHost; @@ -311,7 +311,7 @@ public: /** read and deserialize data, advances msg_bytes data pointer */ virtual int Read(Span<const uint8_t>& msg_bytes) = 0; // decomposes a message from the context - virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0; + virtual std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0; virtual ~TransportDeserializer() {} }; @@ -375,7 +375,7 @@ public: } return ret; } - Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override; + std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override; }; /** The TransportSerializer prepares messages for the network transport @@ -1283,6 +1283,6 @@ struct NodeEvictionCandidate bool m_is_local; }; -[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates); +[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates); #endif // BITCOIN_NET_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp index c569acd3cb..e561f02c4a 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -34,6 +34,7 @@ #include <validation.h> #include <memory> +#include <optional> #include <typeinfo> /** How long to cache transactions in mapRelay for normal relay */ @@ -246,6 +247,7 @@ public: bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override; bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } void SendPings() override; + void RelayTransaction(const uint256& txid, const uint256& wtxid) override; void SetBestHeight(int height) override { m_best_height = height; }; void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override; void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, @@ -259,7 +261,7 @@ private: void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */ - void ReattemptInitialBroadcast(CScheduler& scheduler) const; + void ReattemptInitialBroadcast(CScheduler& scheduler); /** Get a shared pointer to the Peer object. * May return an empty shared_ptr if the Peer object can't be found. */ @@ -474,22 +476,19 @@ private: size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void UpdateBlockAvailability(NodeId nodeid, const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool CanDirectFetch() EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool BlockRequestAllowed(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman); - bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params, + void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv); + bool PrepareBlockFilterRequest(CNode& peer, BlockFilterType filter_type, uint32_t start_height, const uint256& stop_hash, uint32_t max_height_diff, const CBlockIndex*& stop_index, BlockFilterIndex*& filter_index); - void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, - CConnman& connman); - void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, - CConnman& connman); - void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, - CConnman& connman); + void ProcessGetCFilters(CNode& peer, CDataStream& vRecv); + void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv); + void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv); }; } // namespace @@ -575,16 +574,16 @@ struct CNodeState { */ struct ChainSyncTimeoutState { //! A timeout used for checking whether our peer has sufficiently synced - int64_t m_timeout; + int64_t m_timeout{0}; //! A header with the work we require on our peer's chain - const CBlockIndex * m_work_header; + const CBlockIndex* m_work_header{nullptr}; //! After timeout is reached, set to true after sending getheaders - bool m_sent_getheaders; + bool m_sent_getheaders{false}; //! Whether this peer is protected from disconnection due to a bad/slow chain - bool m_protect; + bool m_protect{false}; }; - ChainSyncTimeoutState m_chain_sync{0, nullptr, false, false}; + ChainSyncTimeoutState m_chain_sync; //! Time of last new block announcement int64_t m_last_block_announcement{0}; @@ -598,11 +597,7 @@ struct CNodeState { //! Whether this peer relays txs via wtxid bool m_wtxid_relay{false}; - CNodeState(bool is_inbound) - : m_is_inbound(is_inbound) - { - m_recently_announced_invs.reset(); - } + CNodeState(bool is_inbound) : m_is_inbound(is_inbound) {} }; /** Map maintaining per-node state. */ @@ -732,9 +727,9 @@ bool PeerManagerImpl::TipMayBeStale() return m_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty(); } -bool PeerManagerImpl::CanDirectFetch(const Consensus::Params &consensusParams) +bool PeerManagerImpl::CanDirectFetch() { - return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20; + return m_chainman.ActiveChain().Tip()->GetBlockTime() > GetAdjustedTime() - m_chainparams.GetConsensus().nPowTargetSpacing * 20; } static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -952,7 +947,7 @@ void PeerManagerImpl::InitializeNode(CNode *pnode) } } -void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) const +void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) { std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs(); @@ -961,7 +956,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) const if (tx != nullptr) { LOCK(cs_main); - RelayTransaction(txid, tx->GetWitnessHash(), m_connman); + RelayTransaction(txid, tx->GetWitnessHash()); } else { m_mempool.RemoveUnbroadcastTx(txid, true); } @@ -1197,13 +1192,13 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat // active chain if they are no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about and // we fully-validated them at some point. -bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) +bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex) { AssertLockHeld(cs_main); if (m_chainman.ActiveChain().Contains(pindex)) return true; return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && - (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); + (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT); } std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, BanMan* banman, @@ -1463,9 +1458,9 @@ void PeerManagerImpl::SendPings() for(auto& it : m_peer_map) it.second->m_ping_queued = true; } -void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) +void PeerManagerImpl::RelayTransaction(const uint256& txid, const uint256& wtxid) { - connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + m_connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { AssertLockHeld(::cs_main); CNodeState* state = State(pnode->GetId()); @@ -1533,13 +1528,11 @@ static void RelayAddress(const CNode& originator, connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, const CInv& inv, CConnman& connman) +void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv) { - bool send = false; std::shared_ptr<const CBlock> a_recent_block; std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block; bool fWitnessesPresentInARecentCompactBlock; - const Consensus::Params& consensusParams = chainparams.GetConsensus(); { LOCK(cs_most_recent_block); a_recent_block = most_recent_block; @@ -1565,126 +1558,124 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChain } // release cs_main before calling ActivateBestChain if (need_activate_chain) { BlockValidationState state; - if (!m_chainman.ActiveChainstate().ActivateBestChain(state, chainparams, a_recent_block)) { + if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } LOCK(cs_main); const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash); - if (pindex) { - send = BlockRequestAllowed(pindex, consensusParams); - if (!send) { - LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId()); - } + if (!pindex) { + return; + } + if (!BlockRequestAllowed(pindex)) { + LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId()); + return; } const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); // disconnect node in case we have reached the outbound limit for serving historical blocks - if (send && - connman.OutboundTargetReached(true) && + if (m_connman.OutboundTargetReached(true) && (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) && !pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target ) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId()); - - //disconnect node pfrom.fDisconnect = true; - send = false; + return; } // Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold - if (send && !pfrom.HasPermission(PF_NOBAN) && ( + if (!pfrom.HasPermission(PF_NOBAN) && ( (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) ) )) { - LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId()); - + LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, disconnect peer=%d\n", pfrom.GetId()); //disconnect node and prevent it from stalling (would otherwise wait for the missing block) pfrom.fDisconnect = true; - send = false; + return; } // Pruned nodes may have deleted the block, so check whether // it's available before trying to send. - if (send && (pindex->nStatus & BLOCK_HAVE_DATA)) - { - std::shared_ptr<const CBlock> pblock; - if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) { - pblock = a_recent_block; - } else if (inv.IsMsgWitnessBlk()) { - // Fast-path: in this case it is possible to serve the block directly from disk, - // as the network format matches the format on disk - std::vector<uint8_t> block_data; - if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) { - assert(!"cannot load block from disk"); - } - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data))); - // Don't set pblock as we've sent the block - } else { - // Send block from disk - std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); - if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams)) - assert(!"cannot load block from disk"); - pblock = pblockRead; + if (!(pindex->nStatus & BLOCK_HAVE_DATA)) { + return; + } + std::shared_ptr<const CBlock> pblock; + if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) { + pblock = a_recent_block; + } else if (inv.IsMsgWitnessBlk()) { + // Fast-path: in this case it is possible to serve the block directly from disk, + // as the network format matches the format on disk + std::vector<uint8_t> block_data; + if (!ReadRawBlockFromDisk(block_data, pindex, m_chainparams.MessageStart())) { + assert(!"cannot load block from disk"); + } + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data))); + // Don't set pblock as we've sent the block + } else { + // Send block from disk + std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); + if (!ReadBlockFromDisk(*pblockRead, pindex, m_chainparams.GetConsensus())) { + assert(!"cannot load block from disk"); } - if (pblock) { - if (inv.IsMsgBlk()) { - connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); - } else if (inv.IsMsgWitnessBlk()) { - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); - } else if (inv.IsMsgFilteredBlk()) { - bool sendMerkleBlock = false; - CMerkleBlock merkleBlock; - if (pfrom.m_tx_relay != nullptr) { - LOCK(pfrom.m_tx_relay->cs_filter); - if (pfrom.m_tx_relay->pfilter) { - sendMerkleBlock = true; - merkleBlock = CMerkleBlock(*pblock, *pfrom.m_tx_relay->pfilter); - } - } - if (sendMerkleBlock) { - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); - // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see - // This avoids hurting performance by pointlessly requiring a round-trip - // Note that there is currently no way for a node to request any single transactions we didn't send here - - // they must either disconnect and retry or request the full block. - // Thus, the protocol spec specified allows for us to provide duplicate txn here, - // however we MUST always provide at least what the remote peer needs - typedef std::pair<unsigned int, uint256> PairType; - for (PairType& pair : merkleBlock.vMatchedTxn) - connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); + pblock = pblockRead; + } + if (pblock) { + if (inv.IsMsgBlk()) { + m_connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); + } else if (inv.IsMsgWitnessBlk()) { + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); + } else if (inv.IsMsgFilteredBlk()) { + bool sendMerkleBlock = false; + CMerkleBlock merkleBlock; + if (pfrom.m_tx_relay != nullptr) { + LOCK(pfrom.m_tx_relay->cs_filter); + if (pfrom.m_tx_relay->pfilter) { + sendMerkleBlock = true; + merkleBlock = CMerkleBlock(*pblock, *pfrom.m_tx_relay->pfilter); } - // else - // no response - } else if (inv.IsMsgCmpctBlk()) { - // If a peer is asking for old blocks, we're almost guaranteed - // they won't have a useful mempool to match against a compact block, - // and we don't feel like constructing the object for them, so - // instead we respond with the full, non-compact block. - bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness; - int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - if (CanDirectFetch(consensusParams) && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) { - if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { - connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); - } else { - CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); - connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); - } + } + if (sendMerkleBlock) { + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didn't send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair<unsigned int, uint256> PairType; + for (PairType& pair : merkleBlock.vMatchedTxn) + m_connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); + } + // else + // no response + } else if (inv.IsMsgCmpctBlk()) { + // If a peer is asking for old blocks, we're almost guaranteed + // they won't have a useful mempool to match against a compact block, + // and we don't feel like constructing the object for them, so + // instead we respond with the full, non-compact block. + bool fPeerWantsWitness = State(pfrom.GetId())->fWantsCmpctWitness; + int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; + if (CanDirectFetch() && pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) { + if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { + m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); } else { - connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); + CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); + m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } + } else { + m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); } } + } - { - LOCK(peer.m_block_inv_mutex); - // Trigger the peer node to send a getblocks request for the next batch of inventory - if (inv.hash == peer.m_continuation_block) { - // Send immediately. This must send even if redundant, - // and we want it right after the last block so they don't - // wait for other stuff first. - std::vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash())); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); - peer.m_continuation_block.SetNull(); - } + { + LOCK(peer.m_block_inv_mutex); + // Trigger the peer node to send a getblocks request for the next batch of inventory + if (inv.hash == peer.m_continuation_block) { + // Send immediately. This must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + std::vector<CInv> vInv; + vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash())); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); + peer.m_continuation_block.SetNull(); } } } @@ -1783,7 +1774,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) { const CInv &inv = *it++; if (inv.IsGenBlkMsg()) { - ProcessGetBlockData(pfrom, peer, m_chainparams, inv, m_connman); + ProcessGetBlockData(pfrom, peer, inv); } // else: If the first item on the queue is an unknown type, we erase it // and continue processing the queue on the next call. @@ -1931,10 +1922,9 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256())); } - bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus()); // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. - if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) { + if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) { std::vector<const CBlockIndex*> vToFetch; const CBlockIndex *pindexWalk = pindexLast; // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. @@ -2045,7 +2035,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman); + RelayTransaction(orphanHash, porphanTx->GetWitnessHash()); m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set); m_orphanage.EraseTx(orphanHash); for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) { @@ -2107,7 +2097,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) * May disconnect from the peer in the case of a bad request. * * @param[in] peer The peer that we received the request from - * @param[in] chain_params Chain parameters * @param[in] filter_type The filter type the request is for. Must be basic filters. * @param[in] start_height The start height for the request * @param[in] stop_hash The stop_hash for the request @@ -2116,11 +2105,11 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set) * @param[out] filter_index The filter index, if the request can be serviced. * @return True if the request can be serviced. */ -bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_params, - BlockFilterType filter_type, uint32_t start_height, - const uint256& stop_hash, uint32_t max_height_diff, - const CBlockIndex*& stop_index, - BlockFilterIndex*& filter_index) +bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, + BlockFilterType filter_type, uint32_t start_height, + const uint256& stop_hash, uint32_t max_height_diff, + const CBlockIndex*& stop_index, + BlockFilterIndex*& filter_index) { const bool supported_filter_type = (filter_type == BlockFilterType::BASIC && @@ -2137,7 +2126,7 @@ bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, const CChainParams& stop_index = m_chainman.m_blockman.LookupBlockIndex(stop_hash); // Check that the stop block exists and the peer would be allowed to fetch it. - if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) { + if (!stop_index || !BlockRequestAllowed(stop_index)) { LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n", peer.GetId(), stop_hash.ToString()); peer.fDisconnect = true; @@ -2176,11 +2165,8 @@ bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, const CChainParams& * * @param[in] peer The peer that we received the request from * @param[in] vRecv The raw message received - * @param[in] chain_params Chain parameters - * @param[in] connman Pointer to the connection manager */ -void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, - CConnman& connman) +void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv) { uint8_t filter_type_ser; uint32_t start_height; @@ -2192,7 +2178,7 @@ void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const const CBlockIndex* stop_index; BlockFilterIndex* filter_index; - if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash, + if (!PrepareBlockFilterRequest(peer, filter_type, start_height, stop_hash, MAX_GETCFILTERS_SIZE, stop_index, filter_index)) { return; } @@ -2207,7 +2193,7 @@ void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const for (const auto& filter : filters) { CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion()) .Make(NetMsgType::CFILTER, filter); - connman.PushMessage(&peer, std::move(msg)); + m_connman.PushMessage(&peer, std::move(msg)); } } @@ -2218,11 +2204,8 @@ void PeerManagerImpl::ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const * * @param[in] peer The peer that we received the request from * @param[in] vRecv The raw message received - * @param[in] chain_params Chain parameters - * @param[in] connman Pointer to the connection manager */ -void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, - CConnman& connman) +void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv) { uint8_t filter_type_ser; uint32_t start_height; @@ -2234,7 +2217,7 @@ void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const const CBlockIndex* stop_index; BlockFilterIndex* filter_index; - if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, start_height, stop_hash, + if (!PrepareBlockFilterRequest(peer, filter_type, start_height, stop_hash, MAX_GETCFHEADERS_SIZE, stop_index, filter_index)) { return; } @@ -2263,7 +2246,7 @@ void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const stop_index->GetBlockHash(), prev_header, filter_hashes); - connman.PushMessage(&peer, std::move(msg)); + m_connman.PushMessage(&peer, std::move(msg)); } /** @@ -2273,11 +2256,8 @@ void PeerManagerImpl::ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const * * @param[in] peer The peer that we received the request from * @param[in] vRecv The raw message received - * @param[in] chain_params Chain parameters - * @param[in] connman Pointer to the connection manager */ -void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainParams& chain_params, - CConnman& connman) +void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv) { uint8_t filter_type_ser; uint256 stop_hash; @@ -2288,7 +2268,7 @@ void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const const CBlockIndex* stop_index; BlockFilterIndex* filter_index; - if (!PrepareBlockFilterRequest(peer, chain_params, filter_type, /*start_height=*/0, stop_hash, + if (!PrepareBlockFilterRequest(peer, filter_type, /*start_height=*/0, stop_hash, /*max_height_diff=*/std::numeric_limits<uint32_t>::max(), stop_index, filter_index)) { return; @@ -2314,7 +2294,7 @@ void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const filter_type_ser, stop_index->GetBlockHash(), headers); - connman.PushMessage(&peer, std::move(msg)); + m_connman.PushMessage(&peer, std::move(msg)); } void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, @@ -2952,7 +2932,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, return; } - if (!BlockRequestAllowed(pindex, m_chainparams.GetConsensus())) { + if (!BlockRequestAllowed(pindex)) { LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId()); return; } @@ -3049,7 +3029,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId()); } else { LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId()); - RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman); + RelayTransaction(tx.GetHash(), tx.GetWitnessHash()); } } return; @@ -3064,7 +3044,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // requests for it. m_txrequest.ForgetTxHash(tx.GetHash()); m_txrequest.ForgetTxHash(tx.GetWitnessHash()); - RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman); + RelayTransaction(tx.GetHash(), tx.GetWitnessHash()); m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set); pfrom.nLastTXTime = GetTime(); @@ -3290,8 +3270,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } // If we're not close to tip yet, give up and let parallel block fetch work its magic - if (!fAlreadyInFlight && !CanDirectFetch(m_chainparams.GetConsensus())) + if (!fAlreadyInFlight && !CanDirectFetch()) { return; + } if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) { // Don't bother trying to process compact blocks from v1 peers @@ -3784,17 +3765,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } if (msg_type == NetMsgType::GETCFILTERS) { - ProcessGetCFilters(pfrom, vRecv, m_chainparams, m_connman); + ProcessGetCFilters(pfrom, vRecv); return; } if (msg_type == NetMsgType::GETCFHEADERS) { - ProcessGetCFHeaders(pfrom, vRecv, m_chainparams, m_connman); + ProcessGetCFHeaders(pfrom, vRecv); return; } if (msg_type == NetMsgType::GETCFCHECKPT) { - ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman); + ProcessGetCFCheckPt(pfrom, vRecv); return; } @@ -4109,7 +4090,7 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers() m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; } - if (!m_initial_sync_finished && CanDirectFetch(m_chainparams.GetConsensus())) { + if (!m_initial_sync_finished && CanDirectFetch()) { m_connman.StartExtraBlockRelayPeers(); m_initial_sync_finished = true; } @@ -4222,7 +4203,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) if (pto->m_next_local_addr_send != 0us) { pto->m_addr_known->reset(); } - if (Optional<CAddress> local_addr = GetLocalAddrForPeer(pto)) { + if (std::optional<CAddress> local_addr = GetLocalAddrForPeer(pto)) { FastRandomContext insecure_rand; pto->PushAddress(*local_addr, insecure_rand); } diff --git a/src/net_processing.h b/src/net_processing.h index 6f900410a7..f6f2d73721 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -47,6 +47,10 @@ public: /** Whether this node ignores txs received over p2p. */ virtual bool IgnoresIncomingTxs() = 0; + /** Relay transaction to all peers. */ + virtual void RelayTransaction(const uint256& txid, const uint256& wtxid) + EXCLUSIVE_LOCKS_REQUIRED(cs_main) = 0; + /** Send ping message to all peers */ virtual void SendPings() = 0; @@ -71,7 +75,4 @@ public: const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0; }; -/** Relay transaction to every node */ -void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/netbase.cpp b/src/netbase.cpp index b95bb05e71..49e455aa84 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -166,21 +166,6 @@ static bool LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, un return (vIP.size() > 0); } -/** - * Resolve a host string to its corresponding network addresses. - * - * @param name The string representing a host. Could be a name or a numerical - * IP address (IPv6 addresses in their bracketed form are - * allowed). - * @param[out] vIP The resulting network addresses to which the specified host - * string resolved. - * - * @returns Whether or not the specified host string successfully resolved to - * any resulting network addresses. - * - * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int) - * for additional parameter descriptions. - */ bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { @@ -196,12 +181,6 @@ bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned in return LookupIntern(strHost, vIP, nMaxSolutions, fAllowLookup, dns_lookup_function); } - /** - * Resolve a host string to its first corresponding network address. - * - * @see LookupHost(const std::string&, std::vector<CNetAddr>&, unsigned int, bool) for - * additional parameter descriptions. - */ bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { @@ -215,32 +194,12 @@ bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSL return true; } -/** - * Resolve a service string to its corresponding service. - * - * @param name The string representing a service. Could be a name or a - * numerical IP address (IPv6 addresses should be in their - * disambiguated bracketed form), optionally followed by a port - * number. (e.g. example.com:8333 or - * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420) - * @param[out] vAddr The resulting services to which the specified service string - * resolved. - * @param portDefault The default port for resulting services if not specified - * by the service string. - * @param fAllowLookup Whether or not hostname lookups are permitted. If yes, - * external queries may be performed. - * @param nMaxSolutions The maximum number of results we want, specifying 0 - * means "as many solutions as we get." - * - * @returns Whether or not the service string successfully resolved to any - * resulting services. - */ -bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function) +bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function) { if (name.empty() || !ValidAsCString(name)) { return false; } - int port = portDefault; + uint16_t port{portDefault}; std::string hostname; SplitHostPort(name, port, hostname); @@ -254,13 +213,7 @@ bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefau return true; } -/** - * Resolve a service string to its first corresponding service. - * - * @see Lookup(const char *, std::vector<CService>&, int, bool, unsigned int) - * for additional parameter descriptions. - */ -bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function) +bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { return false; @@ -273,17 +226,7 @@ bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllo return true; } -/** - * Resolve a service string with a numeric IP to its first corresponding - * service. - * - * @returns The resulting CService if the resolution was successful, [::]:0 - * otherwise. - * - * @see Lookup(const char *, CService&, int, bool) for additional parameter - * descriptions. - */ -CService LookupNumeric(const std::string& name, int portDefault, DNSLookupFn dns_lookup_function) +CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(name)) { return {}; @@ -420,25 +363,7 @@ static std::string Socks5ErrorString(uint8_t err) } } -/** - * Connect to a specified destination service through an already connected - * SOCKS5 proxy. - * - * @param strDest The destination fully-qualified domain name. - * @param port The destination port. - * @param auth The credentials with which to authenticate with the specified - * SOCKS5 proxy. - * @param sock The SOCKS5 proxy socket. - * - * @returns Whether or not the operation succeeded. - * - * @note The specified SOCKS5 proxy socket must already be connected to the - * SOCKS5 proxy. - * - * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol - * Version 5</a> - */ -bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock) +bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& sock) { IntrRecvError recvr; LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest); @@ -612,18 +537,6 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg } } -/** - * Try to connect to the specified service on the specified socket. - * - * @param addrConnect The service to which to connect. - * @param hSocket The socket on which to connect. - * @param nTimeout Wait this many milliseconds for the connection to be - * established. - * @param manual_connection Whether or not the connection was manually requested - * (e.g. through the addnode RPC) - * - * @returns Whether or not a connection was successfully made. - */ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection) { // Create a sockaddr from the specified service. @@ -722,22 +635,6 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) { return true; } -/** - * Set the name proxy to use for all connections to nodes specified by a - * hostname. After setting this proxy, connecting to a node specified by a - * hostname won't result in a local lookup of said hostname, rather, connect to - * the node by asking the name proxy for a proxy connection to the hostname, - * effectively delegating the hostname lookup to the specified proxy. - * - * This delegation increases privacy for those who set the name proxy as they no - * longer leak their external hostname queries to their DNS servers. - * - * @returns Whether or not the operation succeeded. - * - * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5 - * server in common use (most notably Tor) actually implements UDP - * support, and a DNS resolver is beyond the scope of this project. - */ bool SetNameProxy(const proxyType &addrProxy) { if (!addrProxy.IsValid()) return false; @@ -768,22 +665,7 @@ bool IsProxy(const CNetAddr &addr) { return false; } -/** - * Connect to a specified destination service through a SOCKS5 proxy by first - * connecting to the SOCKS5 proxy. - * - * @param proxy The SOCKS5 proxy. - * @param strDest The destination service to which to connect. - * @param port The destination port. - * @param sock The socket on which to connect to the SOCKS5 proxy. - * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5 - * proxy to be established. - * @param[out] outProxyConnectionFailed Whether or not the connection to the - * SOCKS5 proxy failed. - * - * @returns Whether or not the operation succeeded. - */ -bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed) +bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed) { // first connect to proxy server if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) { @@ -795,28 +677,17 @@ bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int ProxyCredentials random_auth; static std::atomic_int counter(0); random_auth.username = random_auth.password = strprintf("%i", counter++); - if (!Socks5(strDest, (uint16_t)port, &random_auth, sock)) { + if (!Socks5(strDest, port, &random_auth, sock)) { return false; } } else { - if (!Socks5(strDest, (uint16_t)port, 0, sock)) { + if (!Socks5(strDest, port, 0, sock)) { return false; } } return true; } -/** - * Parse and resolve a specified subnet string into the appropriate internal - * representation. - * - * @param strSubnet A string representation of a subnet of the form `network - * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g. - * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`). - * @param ret The resulting internal representation of a subnet. - * - * @returns Whether the operation succeeded or not. - */ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret, DNSLookupFn dns_lookup_function) { if (!ValidAsCString(strSubnet)) { diff --git a/src/netbase.h b/src/netbase.h index 227da1a63b..08172b9984 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -76,6 +76,22 @@ std::vector<std::string> GetNetworkNames(bool append_unroutable = false); bool SetProxy(enum Network net, const proxyType &addrProxy); bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); +/** + * Set the name proxy to use for all connections to nodes specified by a + * hostname. After setting this proxy, connecting to a node specified by a + * hostname won't result in a local lookup of said hostname, rather, connect to + * the node by asking the name proxy for a proxy connection to the hostname, + * effectively delegating the hostname lookup to the specified proxy. + * + * This delegation increases privacy for those who set the name proxy as they no + * longer leak their external hostname queries to their DNS servers. + * + * @returns Whether or not the operation succeeded. + * + * @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5 + * server in common use (most notably Tor) actually implements UDP + * support, and a DNS resolver is beyond the scope of this project. + */ bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); bool GetNameProxy(proxyType &nameProxyOut); @@ -83,11 +99,83 @@ bool GetNameProxy(proxyType &nameProxyOut); using DNSLookupFn = std::function<std::vector<CNetAddr>(const std::string&, bool)>; extern DNSLookupFn g_dns_lookup; +/** + * Resolve a host string to its corresponding network addresses. + * + * @param name The string representing a host. Could be a name or a numerical + * IP address (IPv6 addresses in their bracketed form are + * allowed). + * @param[out] vIP The resulting network addresses to which the specified host + * string resolved. + * + * @returns Whether or not the specified host string successfully resolved to + * any resulting network addresses. + * + * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a host string to its first corresponding network address. + * + * @see LookupHost(const std::string&, std::vector<CNetAddr>&, uint16_t, bool, DNSLookupFn) + * for additional parameter descriptions. + */ bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); -bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); -bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup); -CService LookupNumeric(const std::string& name, int portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a service string to its corresponding service. + * + * @param name The string representing a service. Could be a name or a + * numerical IP address (IPv6 addresses should be in their + * disambiguated bracketed form), optionally followed by a uint16_t port + * number. (e.g. example.com:8333 or + * [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420) + * @param[out] vAddr The resulting services to which the specified service string + * resolved. + * @param portDefault The default port for resulting services if not specified + * by the service string. + * @param fAllowLookup Whether or not hostname lookups are permitted. If yes, + * external queries may be performed. + * @param nMaxSolutions The maximum number of results we want, specifying 0 + * means "as many solutions as we get." + * + * @returns Whether or not the service string successfully resolved to any + * resulting services. + */ +bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a service string to its first corresponding service. + * + * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ +bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Resolve a service string with a numeric IP to its first corresponding + * service. + * + * @returns The resulting CService if the resolution was successful, [::]:0 otherwise. + * + * @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ +CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup); + +/** + * Parse and resolve a specified subnet string into the appropriate internal + * representation. + * + * @param strSubnet A string representation of a subnet of the form `network + * address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g. + * `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`). + * @param ret The resulting internal representation of a subnet. + * + * @returns Whether the operation succeeded or not. + */ bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet, DNSLookupFn dns_lookup_function = g_dns_lookup); /** @@ -102,14 +190,61 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family); */ extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock; -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection); -bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed); +/** + * Try to connect to the specified service on the specified socket. + * + * @param addrConnect The service to which to connect. + * @param hSocket The socket on which to connect. + * @param nTimeout Wait this many milliseconds for the connection to be + * established. + * @param manual_connection Whether or not the connection was manually requested + * (e.g. through the addnode RPC) + * + * @returns Whether or not a connection was successfully made. + */ +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection); + +/** + * Connect to a specified destination service through a SOCKS5 proxy by first + * connecting to the SOCKS5 proxy. + * + * @param proxy The SOCKS5 proxy. + * @param strDest The destination service to which to connect. + * @param port The destination port. + * @param sock The socket on which to connect to the SOCKS5 proxy. + * @param nTimeout Wait this many milliseconds for the connection to the SOCKS5 + * proxy to be established. + * @param[out] outProxyConnectionFailed Whether or not the connection to the + * SOCKS5 proxy failed. + * + * @returns Whether or not the operation succeeded. + */ +bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed); + /** Disable or enable blocking-mode for a socket */ bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking); /** Set the TCP_NODELAY flag on a socket */ bool SetSocketNoDelay(const SOCKET& hSocket); void InterruptSocks5(bool interrupt); -bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& socket); +/** + * Connect to a specified destination service through an already connected + * SOCKS5 proxy. + * + * @param strDest The destination fully-qualified domain name. + * @param port The destination port. + * @param auth The credentials with which to authenticate with the specified + * SOCKS5 proxy. + * @param sock The SOCKS5 proxy socket. + * + * @returns Whether or not the operation succeeded. + * + * @note The specified SOCKS5 proxy socket must already be connected to the + * SOCKS5 proxy. + * + * @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol + * Version 5</a> + */ +bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& socket); #endif // BITCOIN_NETBASE_H diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 9406ef07c5..50c8c29175 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -50,6 +50,7 @@ #endif #include <memory> +#include <optional> #include <utility> using interfaces::BlockTip; @@ -415,7 +416,7 @@ class ChainImpl : public Chain { public: explicit ChainImpl(NodeContext& node) : m_node(node) {} - Optional<int> getHeight() override + std::optional<int> getHeight() override { LOCK(::cs_main); const CChain& active = Assert(m_node.chainman)->ActiveChain(); @@ -423,7 +424,7 @@ public: if (height >= 0) { return height; } - return nullopt; + return std::nullopt; } uint256 getBlockHash(int height) override { @@ -452,7 +453,7 @@ public: assert(std::addressof(::ChainActive()) == std::addressof(m_node.chainman->ActiveChain())); return CheckFinalTx(m_node.chainman->ActiveChain().Tip(), tx); } - Optional<int> findLocatorFork(const CBlockLocator& locator) override + std::optional<int> findLocatorFork(const CBlockLocator& locator) override { LOCK(cs_main); const CChain& active = Assert(m_node.chainman)->ActiveChain(); @@ -460,7 +461,7 @@ public: if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) { return fork->nHeight; } - return nullopt; + return std::nullopt; } bool findBlock(const uint256& hash, const FoundBlock& block) override { @@ -518,7 +519,7 @@ public: assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman)); return GuessVerificationProgress(Params().TxData(), m_node.chainman->m_blockman.LookupBlockIndex(block_hash)); } - bool hasBlocks(const uint256& block_hash, int min_height, Optional<int> max_height) override + bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override { // hasBlocks returns true if all ancestors of block_hash in specified // range have block data (are not pruned), false if any ancestors in diff --git a/src/node/psbt.h b/src/node/psbt.h index 7384dc415c..def4385c09 100644 --- a/src/node/psbt.h +++ b/src/node/psbt.h @@ -7,6 +7,8 @@ #include <psbt.h> +#include <optional> + /** * Holds an analysis of one input from a PSBT */ @@ -25,18 +27,18 @@ struct PSBTInputAnalysis { * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT) */ struct PSBTAnalysis { - Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction - Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction - Optional<CAmount> fee; //!< Amount of fee being paid by the transaction - std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction - PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next - std::string error; //!< Error message + std::optional<size_t> estimated_vsize; //!< Estimated weight of the transaction + std::optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction + std::optional<CAmount> fee; //!< Amount of fee being paid by the transaction + std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction + PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next + std::string error; //!< Error message void SetInvalid(std::string err_msg) { - estimated_vsize = nullopt; - estimated_feerate = nullopt; - fee = nullopt; + estimated_vsize = std::nullopt; + estimated_feerate = std::nullopt; + fee = std::nullopt; inputs.clear(); next = PSBTRole::CREATOR; error = err_msg; diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 97763f4fa2..f47e85aceb 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -29,9 +29,9 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback) { // BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs. - // node.connman is assigned both before chain clients and before RPC server is accepting calls, - // and reset after chain clients and RPC sever are stopped. node.connman should never be null here. - assert(node.connman); + // node.peerman is assigned both before chain clients and before RPC server is accepting calls, + // and reset after chain clients and RPC sever are stopped. node.peerman should never be null here. + assert(node.peerman); assert(node.mempool); std::promise<void> promise; uint256 hashTx = tx->GetHash(); @@ -101,7 +101,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t node.mempool->AddUnbroadcastTx(hashTx); LOCK(cs_main); - RelayTransaction(hashTx, tx->GetWitnessHash(), *node.connman); + node.peerman->RelayTransaction(hashTx, tx->GetWitnessHash()); } return TransactionError::OK; diff --git a/src/optional.h b/src/optional.h deleted file mode 100644 index 583c56eabd..0000000000 --- a/src/optional.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2017-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_OPTIONAL_H -#define BITCOIN_OPTIONAL_H - -#include <optional> -#include <utility> - -//! Substitute for C++17 std::optional -//! DEPRECATED use std::optional in new code. -template <typename T> -using Optional = std::optional<T>; - -//! Substitute for C++17 std::nullopt -//! DEPRECATED use std::nullopt in new code. -static auto& nullopt = std::nullopt; - -#endif // BITCOIN_OPTIONAL_H diff --git a/src/psbt.h b/src/psbt.h index b566726ee3..96ae39fdb8 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -7,13 +7,14 @@ #include <attributes.h> #include <node/transaction.h> -#include <optional.h> #include <policy/feerate.h> #include <primitives/transaction.h> #include <pubkey.h> #include <script/sign.h> #include <script/signingprovider.h> +#include <optional> + // Magic bytes static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff}; @@ -389,7 +390,7 @@ struct PSBTOutput /** A version of CTransaction with the PSBT format*/ struct PartiallySignedTransaction { - Optional<CMutableTransaction> tx; + std::optional<CMutableTransaction> tx; std::vector<PSBTInput> inputs; std::vector<PSBTOutput> outputs; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index ab6168a541..9927e925ac 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -295,7 +295,7 @@ void AddressBookPage::on_exportButton_clicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(), - tr("Comma separated file (*.csv)"), nullptr); + tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index f25b415820..791a29f7a0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -52,7 +52,6 @@ #include <QThread> #include <QTimer> #include <QTranslator> -#include <QtGlobal> #if defined(QT_STATICPLUGIN) #include <QtPlugin> @@ -467,13 +466,6 @@ int GuiMain(int argc, char* argv[]) QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#if (QT_VERSION <= QT_VERSION_CHECK(5, 9, 8)) && defined(Q_OS_MACOS) - const auto os_name = QSysInfo::prettyProductName(); - if (os_name.startsWith("macOS 11") || os_name.startsWith("macOS 10.16")) { - QApplication::setStyle("fusion"); - } -#endif - BitcoinApplication app; QFontDatabase::addApplicationFont(":/fonts/monospace"); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 1cc96035c6..ba35ff3b67 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -67,7 +67,7 @@ private Q_SLOTS: void updateDefaultProxyNets(); Q_SIGNALS: - void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort); + void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort); private: Ui::OptionsDialog *ui; diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index d1f8d56100..17746b395b 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -141,7 +141,7 @@ void PSBTOperationsDialog::saveTransaction() { filename_suggestion.append(".psbt"); QString filename = GUIUtil::getSaveFileName(this, tr("Save Transaction Data"), filename_suggestion, - tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter); + tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selected_filter); if (filename.isEmpty()) { return; } diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp index 490826cbbb..a71c8831e9 100644 --- a/src/qt/qrimagewidget.cpp +++ b/src/qt/qrimagewidget.cpp @@ -120,7 +120,9 @@ void QRImageWidget::saveImage() { if (!GUIUtil::HasPixmap(this)) return; - QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr); + QString fn = GUIUtil::getSaveFileName( + this, tr("Save QR Code"), QString(), + tr("PNG Image", "Name of PNG file format") + QLatin1String(" (*.png)"), nullptr); if (!fn.isEmpty()) { exportImage().save(fn); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index e401f46696..95e1ce2210 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -430,7 +430,7 @@ void SendCoinsDialog::on_sendButton_clicked() fileNameSuggestion.append(".psbt"); QString filename = GUIUtil::getSaveFileName(this, tr("Save Transaction Data"), fileNameSuggestion, - tr("Partially Signed Transaction (Binary) (*.psbt)"), &selectedFilter); + tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selectedFilter); if (filename.isEmpty()) { return; } diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index 35fcb2b0ca..a026069232 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -112,7 +112,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node) ClientModel clientModel(node, &optionsModel); AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get()); - RemoveWallet(wallet, nullopt); + RemoveWallet(wallet, std::nullopt); EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index d6d2d0e3df..1107c44dc9 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -167,7 +167,7 @@ void TestGUI(interfaces::Node& node) ClientModel clientModel(node, &optionsModel); AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get()); - RemoveWallet(wallet, nullopt); + RemoveWallet(wallet, std::nullopt); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index f99a3e286d..42e08c6af7 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -357,7 +357,7 @@ void TransactionView::exportClicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Transaction History"), QString(), - tr("Comma separated file (*.csv)"), nullptr); + tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index b1e6b43e60..8612893683 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -273,7 +273,7 @@ void WalletView::backupWallet() { QString filename = GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(), - tr("Wallet Data (*.dat)"), nullptr); + tr("Wallet Data", "Name of wallet data file format") + QLatin1String(" (*.dat)"), nullptr); if (filename.isEmpty()) return; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index cf4d46cf2c..d4c1ab4b53 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -914,7 +914,7 @@ static RPCHelpMan addpeeraddress() UniValue obj(UniValue::VOBJ); std::string addr_string = request.params[0].get_str(); - uint16_t port = request.params[1].get_int(); + uint16_t port{static_cast<uint16_t>(request.params[1].get_int())}; CNetAddr net_addr; if (!LookupHost(addr_string, net_addr, false)) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 47c776bbd1..2f92a321f8 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1828,13 +1828,13 @@ static RPCHelpMan analyzepsbt() } if (!inputs_result.empty()) result.pushKV("inputs", inputs_result); - if (psbta.estimated_vsize != nullopt) { + if (psbta.estimated_vsize != std::nullopt) { result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize); } - if (psbta.estimated_feerate != nullopt) { + if (psbta.estimated_feerate != std::nullopt) { result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK())); } - if (psbta.fee != nullopt) { + if (psbta.fee != std::nullopt) { result.pushKV("fee", ValueFromAmount(*psbta.fee)); } result.pushKV("next", PSBTRoleName(psbta.next)); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 889cb4f06e..30399dca51 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -17,6 +17,7 @@ #include <util/vector.h> #include <memory> +#include <optional> #include <string> #include <vector> @@ -632,7 +633,7 @@ public: } } - Optional<OutputType> GetOutputType() const override { return nullopt; } + std::optional<OutputType> GetOutputType() const override { return std::nullopt; } }; /** A parsed addr(A) descriptor. */ @@ -646,7 +647,7 @@ public: AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {} bool IsSolvable() const final { return false; } - Optional<OutputType> GetOutputType() const override + std::optional<OutputType> GetOutputType() const override { switch (m_destination.index()) { case 1 /* PKHash */: @@ -655,7 +656,7 @@ public: case 4 /* WitnessV0KeyHash */: case 5 /* WitnessUnknown */: return OutputType::BECH32; case 0 /* CNoDestination */: - default: return nullopt; + default: return std::nullopt; } } bool IsSingleType() const final { return true; } @@ -672,7 +673,7 @@ public: RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {} bool IsSolvable() const final { return false; } - Optional<OutputType> GetOutputType() const override + std::optional<OutputType> GetOutputType() const override { CTxDestination dest; ExtractDestination(m_script, dest); @@ -683,7 +684,7 @@ public: case 4 /* WitnessV0KeyHash */: case 5 /* WitnessUnknown */: return OutputType::BECH32; case 0 /* CNoDestination */: - default: return nullopt; + default: return std::nullopt; } } bool IsSingleType() const final { return true; } @@ -711,7 +712,7 @@ protected: } public: PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {} - Optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; } + std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; } bool IsSingleType() const final { return true; } }; @@ -727,7 +728,7 @@ protected: } public: WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {} - Optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } + std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } bool IsSingleType() const final { return true; } }; @@ -783,7 +784,7 @@ protected: public: SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {} - Optional<OutputType> GetOutputType() const override + std::optional<OutputType> GetOutputType() const override { assert(m_subdescriptor_arg); if (m_subdescriptor_arg->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT; @@ -799,7 +800,7 @@ protected: std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(WitnessV0ScriptHash(*script))); } public: WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {} - Optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } + std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } bool IsSingleType() const final { return true; } }; diff --git a/src/script/descriptor.h b/src/script/descriptor.h index 46d51fa587..332ae2f230 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -5,12 +5,12 @@ #ifndef BITCOIN_SCRIPT_DESCRIPTOR_H #define BITCOIN_SCRIPT_DESCRIPTOR_H -#include <optional.h> #include <outputtype.h> #include <script/script.h> #include <script/sign.h> #include <script/signingprovider.h> +#include <optional> #include <vector> using ExtPubKeyMap = std::unordered_map<uint32_t, CExtPubKey>; @@ -124,7 +124,7 @@ struct Descriptor { virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0; /** @return The OutputType of the scriptPubKey(s) produced by this descriptor. Or nullopt if indeterminate (multiple or none) */ - virtual Optional<OutputType> GetOutputType() const = 0; + virtual std::optional<OutputType> GetOutputType() const = 0; }; /** Parse a `descriptor` string. Included private keys are put in `out`. diff --git a/src/signet.cpp b/src/signet.cpp index e68f031aa4..fb41e43752 100644 --- a/src/signet.cpp +++ b/src/signet.cpp @@ -65,7 +65,7 @@ static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CB return ComputeMerkleRoot(std::move(leaves)); } -Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challenge) +std::optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challenge) { CMutableTransaction tx_to_spend; tx_to_spend.nVersion = 0; @@ -83,12 +83,12 @@ Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challe // responses from block coinbase tx // find and delete signet signature - if (block.vtx.empty()) return nullopt; // no coinbase tx in block; invalid + if (block.vtx.empty()) return std::nullopt; // no coinbase tx in block; invalid CMutableTransaction modified_cb(*block.vtx.at(0)); const int cidx = GetWitnessCommitmentIndex(block); if (cidx == NO_WITNESS_COMMITMENT) { - return nullopt; // require a witness commitment + return std::nullopt; // require a witness commitment } CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey; @@ -101,9 +101,9 @@ Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challe VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0); v >> tx_spending.vin[0].scriptSig; v >> tx_spending.vin[0].scriptWitness.stack; - if (!v.empty()) return nullopt; // extraneous data encountered + if (!v.empty()) return std::nullopt; // extraneous data encountered } catch (const std::exception&) { - return nullopt; // parsing error + return std::nullopt; // parsing error } } uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block); @@ -129,7 +129,7 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons } const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end()); - const Optional<SignetTxs> signet_txs = SignetTxs::Create(block, challenge); + const std::optional<SignetTxs> signet_txs = SignetTxs::Create(block, challenge); if (!signet_txs) { LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n"); diff --git a/src/signet.h b/src/signet.h index 23563a83c4..f876488c0a 100644 --- a/src/signet.h +++ b/src/signet.h @@ -9,7 +9,7 @@ #include <primitives/block.h> #include <primitives/transaction.h> -#include <optional.h> +#include <optional> /** * Extract signature and check whether a block has a valid solution @@ -28,7 +28,7 @@ class SignetTxs { SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign} { } public: - static Optional<SignetTxs> Create(const CBlock& block, const CScript& challenge); + static std::optional<SignetTxs> Create(const CBlock& block, const CScript& challenge); const CTransaction m_to_spend; const CTransaction m_to_sign; diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 37ff8a9afe..d438537606 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -100,7 +100,7 @@ static CNetAddr ResolveIP(const std::string& ip) return addr; } -static CService ResolveService(const std::string& ip, const int port = 0) +static CService ResolveService(const std::string& ip, uint16_t port = 0) { CService serv; BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port)); diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp index a2098f4f56..2651e46430 100644 --- a/src/test/bech32_tests.cpp +++ b/src/test/bech32_tests.cpp @@ -10,7 +10,7 @@ BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup) -BOOST_AUTO_TEST_CASE(bip173_testvectors_valid) +BOOST_AUTO_TEST_CASE(bech32_testvectors_valid) { static const std::string CASES[] = { "A12UEL5L", @@ -22,15 +22,35 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_valid) "?1ezyfcl", }; for (const std::string& str : CASES) { - auto ret = bech32::Decode(str); - BOOST_CHECK(!ret.first.empty()); - std::string recode = bech32::Encode(ret.first, ret.second); + const auto dec = bech32::Decode(str); + BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32); + std::string recode = bech32::Encode(bech32::Encoding::BECH32, dec.hrp, dec.data); BOOST_CHECK(!recode.empty()); BOOST_CHECK(CaseInsensitiveEqual(str, recode)); } } -BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) +BOOST_AUTO_TEST_CASE(bech32m_testvectors_valid) +{ + static const std::string CASES[] = { + "A1LQFN3A", + "a1lqfn3a", + "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", + "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", + "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", + "split1checkupstagehandshakeupstreamerranterredcaperredlc445v", + "?1v759aa" + }; + for (const std::string& str : CASES) { + const auto dec = bech32::Decode(str); + BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32M); + std::string recode = bech32::Encode(bech32::Encoding::BECH32M, dec.hrp, dec.data); + BOOST_CHECK(!recode.empty()); + BOOST_CHECK(CaseInsensitiveEqual(str, recode)); + } +} + +BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid) { static const std::string CASES[] = { " 1nwldj5", @@ -49,8 +69,32 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) "A12uEL5L", }; for (const std::string& str : CASES) { - auto ret = bech32::Decode(str); - BOOST_CHECK(ret.first.empty()); + const auto dec = bech32::Decode(str); + BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID); + } +} + +BOOST_AUTO_TEST_CASE(bech32m_testvectors_invalid) +{ + static const std::string CASES[] = { + " 1xj0phk", + "\x7f""1g6xzxy", + "\x80""1vctc34", + "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", + "qyrz8wqd2c9m", + "1qyrz8wqd2c9m", + "y1b0jsk6g", + "lt1igcx5c0", + "in1muywd", + "mm1crxm3i", + "au1s5cgom", + "M1VUXWEZ", + "16plkw9", + "1p2gdwpf" + }; + for (const std::string& str : CASES) { + const auto dec = bech32::Decode(str); + BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID); } } diff --git a/src/test/data/key_io_invalid.json b/src/test/data/key_io_invalid.json index 9b52943ac6..abe07dad24 100644 --- a/src/test/data/key_io_invalid.json +++ b/src/test/data/key_io_invalid.json @@ -6,177 +6,207 @@ "x" ], [ - "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y" + "2v7k5Bb8Lr1MMgTgW6HAf5YHXi6BzpPjHpQ4srD4RSwHYpzXKiXmLAgiLhkXvp3JF5v7nq45EWr" ], [ - "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv" + "RAZzCGtMbiUgMiiyrZySrSpdfnQReFXA3r" ], [ - "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S" + "NYamy7tcPQTzoU5iyQojD3sqhiz7zxkvn8" ], [ - "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf" + "geaFG555Ex5nyRf7JjW6Pj2GwZA8KYxtJJLbr1eZhVW75STbYBZeRszy3wg4pkKdF4ez9J4wQiz" ], [ - "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq" + "2Cxmid3c2XQ2zvQ8SA1ha2TKqvqbJS9XFmXRsCneBS3Po7Qqb65z5zNdsoF9AfieXFcpoVPmkmfa" ], [ - "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb" + "gaJ7UVge2njVg9tFTetJrtHgruMm7aQDiSAxfHrVEgzK8N2ooagDVmDkdph434xzc4K96Gjyxcs" ], [ - "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs" + "5JN5BEVQPZ3tAiatz1RGXkrJuE3EC6bervMaPb38wTNgEuZCeqp" ], [ - "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3" + "3TnFbyUtBRS5rE1KTW81qLVspjJNaB3uu6uuvLjxhZo2DB6PCGh" ], [ - "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th" + "7UgSZGaMaTc4d2mdEgcGBFiMeS6eMsithGUqvBsKTQdGzD7XQDbMEYo3gojdbXEPbUdFF3CQoK72f" ], [ - "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va" + "9261wfqQqruNDnBDhbbb4tN9oKA1KpRFHeoYeufyJApVGixyAG4V" ], [ - "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk" + "cS824CTUh18scFmYuqt6BgxuRhdR4dEEnCHs3fzBbcyQgbfasHbw" ], [ - "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs" + "tc1q0ywf7wkz6t580n3yemd3ucfw8jxn93tpc6wskt" ], [ - "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo" + "bt1pxeeuh96wpm5c6u3kavts2qgwlv6y8um7u7ga6ltlwrhrv7w9vers8lgt3k" ], [ - "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso" + "tb130lvl2lyugsk2tf3zhwcjjv39dmwt2tt7ytqaexy8edwcuwks6p5scll5kz" ], [ - "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq" + "bcrt1rhsveeudk" ], [ - "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN" + "bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p" ], [ - "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i" + "tb1qjqnfsuatr54e957xzg9sqk7yqcry9lns" ], [ - "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos" + "bcrt1q8p08mv8echkf3es027u4cdswxlylm3th76ls8v6y4zy4vwsavngpr4e4td" ], [ - "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu" + "BC1QNC2H66VLWTWTW52DP0FYUSNU3QQG5VT4V" ], [ - "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb" + "tb1qgk665m2auw09rc7pqyf7aulcuhmatz9xqtr5mxew7zuysacaascqs9v0vn" ], [ - "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ" + "bcrt17CAPP7" ], [ - "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ" + "bc1qxmf2d6aerjzam3rur0zufqxqnyqfts5u302s7x" ], [ - "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu" + "tb1qn8x5dnzpexq7nnvrvnhwr9c3wkakpcyu9wwsjzq9pstkwg0t6qhs4l3rv6" ], [ - "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU" + "BCRT1Q397G2RNVYRL5LK07CE8NCKHVKP8Z4SC9U0MVH9" ], [ - "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs" + "bc1pgxwyajq0gdn389f69uwn2fw9q0z5c9s063j5dgkdd23ajaud4hpsercr9h" ], [ - "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn" + "tb1z6mnmp5k542l6yk4ul0mp4rq3yvz44lfm" ], [ - "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj" + "bcrt17capp7" ], [ - "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny" + "2D2bqvKseKHdoKjCNvjVULUgmxHu9hjKGwDbPRjTRH59tsHNLeyKwq3vyVBbo9LByY9wiapqjwFY" ], [ - "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc" + "2SSjAim4wZpeQRe5zTj1qqS6Li9ttJDaZ3ze" ], [ - "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ" + "mi9H6MjLwXxy9kxe1x4ToxyLRBsmcZxgVi" ], [ - "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP" + "VciXoxEitcn88jy197J9n9cpJ1pZahzU3SyWUiHqLgcfjttLEEJz" ], [ - "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw" + "KppmwADGoExPT9Eq5hjRWpWFDbzJyfzHFgsfxBiDHNpVBgWPRNuy" ], [ - "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX" + "TN7EQXMxKffzvHo54yHHu9R4ks9f5gWBW3MMVf5k72zAqrgVK9ys" ], [ - "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB" + "92dbrMEYzP5dD5UhQ6maNkCQ4GLG42BM4Gc6XKZzSSMSfosfkkcB" ], [ - "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ" + "J7VQxPxyzuWEkRstQWpCz2AgysEz1APgnWCEQrFvkN3umAnCrhQF" ], [ - "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs" + "tc1qymllj6c96v5qj2504y27ldtner6eh8ldx38t83" ], [ - "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ" + "bt1flep4g" ], [ - "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4" + "tb13c553hwygcgj48qwmr9f8q0hgdcfklyaye5sxzcpcjnmxv4z506xs90tchn" ], [ - "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK" + "bcrt1tyddyu" ], [ - "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig" + "bc10qssq2mknjqf0glwe2f3587wc4jysvs3f8s6chysae6hcl6fxzdm4wxyyscrl5k9f5qmnf05a" ], [ - "EsYbG4tWWWY45G31nox838qNdzksbPySWc" + "tb1q425lmgvxdgtyl2m6xuu2pc354y4fvgg8" ], [ - "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT" + "bcrt1q9wp8e5d2u3u4g0pll0cy7smeeuqezdun9xl439n3p2gg4fvgfvk3hu52hj" ], [ - "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx" + "bc1qrz5acazpue8vl4zsaxn8fxtmeuqmyjkq3" ], [ - "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde" + "tb1qkeuglpgmnex9tv3fr7htzfrh3rwrk23r52rx9halxzmv9fr85lwq0fwhmp" ], [ - "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU" + "bcrt1qd0t2wrhl7s57z99rsyaekpq0dyjcQRSSmz80r4" ], [ - "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf" + "BC1QXLFDUCGX90T3E53PQCNKJ2PK25MSF3VLPMVY6T" ], [ - "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd" + "tb1qmycg4zszgnk34vaurx3cu8wpvteg9h40yq6cp52gt26gjel03t3su3x3xu" ], [ - "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" + "bcrt1q9hy58r4fnuxqzdqndpmq9pptc9nt2dw3rczf5e" ], [ - "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty" + "BC1PA7682NAY6JQSLUWAJYTC0ERWTMW7A4RPWLNTUS32LCXWLHVKKKTQ2UL8CG" ], [ - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5" + "tb1z850dpxnwz2fzae5h2myatj4yvu6rq5xq" ], [ - "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2" + "bcrt1sp525pzjsmpqvcrawjreww36e9keg876skjvpwt" ], [ - "bc1rw5uspcuh" + "xcAvW5jurCpzSpLxBKEhCewCgwwuGhqJnC" ], [ - "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90" + "2Cvv8yp9kXbQt8EKh6Yma95yJ1uwYF9YKXuVhGJyu3dHGVsb2AVpTC62TFACZZ3KDNrALxR2CVNs" ], [ - "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P" + "niUuL46hCuEVvkAzZKHvD746qbmLmzip9Pv3F6UZV14JxzEXBnTkVxCT4URapChJG6qAEgsZs6G" ], [ - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" + "2UHHgGfiipzvB8Eumnmvq6SowvrMJimjT3NwwG1839XEiUfwtpSdkUrseNsQuagXv21ce7aZu6yo" ], [ - "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du" + "8u9djKu4u6o3bsgeR4BKNnLK3akpo64FYzDAmA9239wKeshgF97" ], [ - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv" + "TC1QPAARXSLVMXHVRR0474LZXQYZWLGFZYPSFVL9E4" ], [ - "bc1gmk9yu" + "bt1pakek0n2267t9yaksxaczgr2syhv9y3xkx0wnsdwchfa6xkmjtvuqg3kgyr" + ], + [ + "tb13h83rtwq62udrhwpn87uely7cyxcjrj0azz6a4r3n9s87x5uj98ys6ufp83" + ], + [ + "bcrt1rk5vw5qf2" + ], + [ + "bc10d3rmtg62h747en5j6fju5g5qyvsransrkty6ghh96pu647wumctejlsngh9pf26cysrys2x2" + ], + [ + "tb1qajuy2cdwqgmrzc7la85al5cwcq374tsp" + ], + [ + "bcrt1q3udxvj6x20chqh723mn064mzz65yr56ef00xk8czvu3jnx04ydapzk02s5" + ], + [ + "bc1qule2szwzyaq4qy0s3aa4mauucyqt6fewe" + ], + [ + "tb1ql0qny5vg9gh5tyzke6dw36px5ulkrp24x53x0pl2t5lpwrtejw3s2seej2" + ], + [ + "bcrt17CAPP7" + ], + [ + "bc1qtvm6davyf725wfedc2d5mrgfewqgcrce8gjrpl" + ], + [ + "tb1q5acjgtqrrw3an0dzavxxxzlex8k7aukjzjk9v2u4rmfdqxjphcyq7ge97e" ] ] diff --git a/src/test/data/key_io_valid.json b/src/test/data/key_io_valid.json index 8418a6002d..5dee44c04b 100644 --- a/src/test/data/key_io_valid.json +++ b/src/test/data/key_io_valid.json @@ -1,533 +1,610 @@ [ [ - "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", - "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", + "1BShJZ8A5q53oJJfMJoEF1gfZCWdZqZwwD", + "76a914728d4cc27d19707b0197cfcd7c412d43287864b588ac", { - "isPrivkey": false, - "chain": "main" + "chain": "main", + "isPrivkey": false } ], [ - "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", - "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", + "3L1YkZjdeNSqaZcNKZFXQfyokx3zVYm7r6", + "a914c8f37c3cc21561296ad81f4bec6b5de10ebc185187", { - "isPrivkey": false, - "chain": "main" + "chain": "main", + "isPrivkey": false } ], [ - "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", - "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", + "mhJuoGLgnJC8gdBgBzEigsoyG4omQXejPT", + "76a91413a92d1998e081354d36c13ce0c9dc04b865d40a88ac", { - "isPrivkey": false, - "chain": "test" + "chain": "test", + "isPrivkey": false } ], [ - "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", - "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", + "2N5VpzKEuYvZJbmg6eUNGnfrrD1ir92FWGu", + "a91486648cc2faaf05660e72c04c7a837bcc3e986f1787", { - "isPrivkey": false, - "chain": "regtest" + "chain": "test", + "isPrivkey": false } ], [ - "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", - "a9146349a418fc4578d10a372b54b45c280cc8c4382f87", + "mtQueCtmAnP3E4aBHXCiFNEQAuPaLMuQNy", + "76a9148d74ecd86c845baf9c6d4484d2d00e731b79e34788ac", { - "isPrivkey": false, - "chain": "test" + "chain": "signet", + "isPrivkey": false + } + ], + [ + "2NEvWRTHjh89gV52fkperFtwzoFWQiQmiCh", + "a914edc895152c67ccff0ba620bcc373b789ec68266f87", + { + "chain": "signet", + "isPrivkey": false } ], [ - "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", - "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", + "mngdx94qJFhSf7A7SAEgQSC9fQJuapujJp", + "76a9144e9dba545455a80ce94c343d1cac9dec62cbf22288ac", { + "chain": "regtest", + "isPrivkey": false + } + ], + [ + "2NBzRN3pV56k3JUvSHifaHyzjGHv7ZS9FZZ", + "a914cd9da5642451273e5b6d088854cc1fad4a8d442187", + { + "chain": "regtest", + "isPrivkey": false + } + ], + [ + "5KcrFZvJ2p4dM6QVUPu53cKXcCfozA1PJLHm1mNAxkDYhgThLu4", + "ed6c796e2f62377410766214f55aa81ac9a6590ad7ed57c509c983bf648409ac", + { + "chain": "main", "isCompressed": false, - "isPrivkey": true, - "chain": "main" + "isPrivkey": true } ], [ - "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", - "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", + "L195WBrf2G3nCnun4CLxrb8XKk9LbCqH43THh4n4QrL5SzRzpq9j", + "74f76c106e38d20514a99a86e4fe3bb28319e7dd2ad21dbc170cbb516a5358fa", { + "chain": "main", "isCompressed": true, - "isPrivkey": true, - "chain": "main" + "isPrivkey": true } ], [ - "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", - "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "92z6HnMQR4tWqjfVA3UaUN5EuUMgoVMdCa5rZFYZfmgyD7wxYCw", + "b8511e1d74549e305517d48a1d394d1be2cfa5d0f3c0d83f9f450316ffa01276", { + "chain": "test", "isCompressed": false, - "isPrivkey": true, - "chain": "test" + "isPrivkey": true } ], [ - "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", - "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "cTPnaF52x4w4Tq6afPxRHux3wbYb86thS7S45A7r3oZc1AHTQ6Qm", + "ad68c48d337181da125de9061933ececcdf7d917631af7d34f7e38082bff9a11", { + "chain": "test", + "isCompressed": true, + "isPrivkey": true + } + ], + [ + "924U35yFcYkxe2JXGmuhSRVaShGyhRDZx1ysPmw1sAHuszGMoxq", + "3e8dfaf78d4f02b11d0b645648a4f3080d71d0d068979c47f7255c9a29eee01d", + { + "chain": "signet", "isCompressed": false, - "isPrivkey": true, - "chain": "regtest" + "isPrivkey": true } ], [ - "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", - "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + "cRy1qCf2LUesGPQagTkYwk2V3PyN2KCPKgxeg6k6KoJPzH7nrVjw", + "82d4187690d6b59bcffda27dae52f2ecb87313cfc0904e0b674a27d906a65fde", { + "chain": "signet", "isCompressed": true, - "isPrivkey": true, - "chain": "test" + "isPrivkey": true } ], [ - "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", - "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + "932NTcHK35Apf2C3K9Zv1ZdeZEmB1x7ZT2Ju3SjoEY6pUgUpT7H", + "bd7dba24df9e003e145ae9b4862776413a0bb6fa5b4e42753397f2d9536e58a9", { - "isCompressed": true, - "isPrivkey": true, - "chain": "regtest" + "chain": "regtest", + "isCompressed": false, + "isPrivkey": true } ], [ - "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", - "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac", + "cNa75orYQ2oos52zCnMaS5PG6XbNZKc5LpGxTHacrxwWeX4WAK3E", + "1d87e3c58b08766fea03598380ec8d59f8c88d5392bf683ab1088bd4caf073ee", { - "isPrivkey": false, - "chain": "main" + "chain": "regtest", + "isCompressed": true, + "isPrivkey": true } ], [ - "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", - "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587", + "bc1q5cuatynjmk4szh40mmunszfzh7zrc5xm9w8ccy", + "0014a639d59272ddab015eafdef9380922bf843c50db", { + "chain": "main", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", - "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac", + "bc1qkw7lz3ahms6e0ajv27mzh7g62tchjpmve4afc29u7w49tddydy2syv0087", + "0020b3bdf147b7dc3597f64c57b62bf91a52f179076ccd7a9c28bcf3aa55b5a46915", { + "chain": "main", "isPrivkey": false, - "chain": "test" + "tryCaseFlip": true } ], [ - "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - "a914c579342c2c4c9220205e2cdc285617040c924a0a87", + "bc1p5rgvqejqh9dh37t9g94dd9cm8vtqns7dndgj423egwggsggcdzmsspvr7j", + "5120a0d0c06640b95b78f965416ad6971b3b1609c3cd9b512aaa39439088211868b7", { + "chain": "main", "isPrivkey": false, - "chain": "test" + "tryCaseFlip": true } ], [ - "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", - "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", + "bc1zr4pq63udck", + "52021d42", { - "isCompressed": false, - "isPrivkey": true, - "chain": "main" + "chain": "main", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", - "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", + "tb1q74fxwnvhsue0l8wremgq66xzvn48jlc5zthsvz", + "0014f552674d978732ff9dc3ced00d68c264ea797f14", { - "isCompressed": true, - "isPrivkey": true, - "chain": "main" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", - "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + "tb1qpt7cqgq8ukv92dcraun9c3n0s3aswrt62vtv8nqmkfpa2tjfghesv9ln74", + "00200afd802007e598553703ef265c466f847b070d7a5316c3cc1bb243d52e4945f3", { - "isCompressed": false, - "isPrivkey": true, - "chain": "test" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", - "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", + "tb1ph9v3e8nxct57hknlkhkz75p5pnxnkn05cw8ewpxu6tek56g29xgqydzfu7", + "5120b9591c9e66c2e9ebda7fb5ec2f50340ccd3b4df4c38f9704dcd2f36a690a2990", { - "isCompressed": true, - "isPrivkey": true, - "chain": "test" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", - "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac", + "tb1ray6e8gxfx49ers6c4c70l3c8lsxtcmlx", + "5310e93593a0c9354b91c358ae3cffc707fc", { + "chain": "test", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", - "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87", + "tb1q0sqzfp3zj42u0perxr6jahhu4y03uw4dypk6sc", + "00147c002486229555c7872330f52edefca91f1e3aad", { + "chain": "signet", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", - "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac", + "tb1q9jv4qnawnuevqaeadn47gkq05ev78m4qg3zqejykdr9u0cm7yutq6gu5dj", + "00202c99504fae9f32c0773d6cebe4580fa659e3eea044440cc89668cbc7e37e2716", { + "chain": "signet", "isPrivkey": false, - "chain": "test" + "tryCaseFlip": true } ], [ - "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", - "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087", + "tb1pxqf7d825wjtcftj7uep8w24jq3tz8vudfaqj20rns8ahqya56gcs92eqtu", + "51203013e69d54749784ae5ee642772ab2045623b38d4f41253c7381fb7013b4d231", { + "chain": "signet", "isPrivkey": false, - "chain": "test" + "tryCaseFlip": true } ], [ - "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", - "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", + "tb1rsrzkyvu2rt0dcgexajtazlw5nft4j7494ay396q6auw9375wxsrsgag884", + "532080c562338a1adedc2326ec97d17dd49a57597aa5af4912e81aef1c58fa8e3407", { - "isCompressed": false, - "isPrivkey": true, - "chain": "main" + "chain": "signet", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", - "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", + "bcrt1qwf52dt9y2sv0f7fwkcpmtfjf74d4np2saeljt6", + "00147268a6aca45418f4f92eb603b5a649f55b598550", { - "isCompressed": true, - "isPrivkey": true, - "chain": "main" + "chain": "regtest", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", - "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + "bcrt1q0lma84unycxl4n96etffthqlf7y5axyp4fxf64kmhymvw8l6pwfs39futd", + "00207ff7d3d793260dfaccbacad295dc1f4f894e9881aa4c9d56dbb936c71ffa0b93", { - "isCompressed": false, - "isPrivkey": true, - "chain": "test" + "chain": "regtest", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", - "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", + "bcrt1p3xat2ryucc2v0adrktqnavfzttvezrr27ngltsa2726p2ehvxz4se722v2", + "512089bab50c9cc614c7f5a3b2c13eb1225ad9910c6af4d1f5c3aaf2b41566ec30ab", { - "isCompressed": true, - "isPrivkey": true, - "chain": "test" + "chain": "regtest", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", - "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac", + "bcrt1saflydw6e26xhp29euhy5jke5jjqyywk3wvtc9ulgw9dvxyuqy9hdnxthyw755c7ldavy7u", + "6028ea7e46bb59568d70a8b9e5c9495b349480423ad1731782f3e8715ac31380216ed9997723bd4a63df", { + "chain": "regtest", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", - "a914188f91a931947eddd7432d6e614387e32b24470987", + "16y3Q1XVRZqMR9T1XL1FkvNtD2E1bXBuYa", + "76a9144171ec673aeb9fcf42af6094a3c82207e3b9a78188ac", { - "isPrivkey": false, - "chain": "main" + "chain": "main", + "isPrivkey": false } ], [ - "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", - "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac", + "3CmZZnAiHVQgiAKSakf864oJMxN2BP1eLC", + "a914798575fc1041b9440c4e63c28e57e597d00b7e4387", { - "isPrivkey": false, - "chain": "test" + "chain": "main", + "isPrivkey": false } ], [ - "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", - "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387", + "mtCB3SoBo7EYUv8j54kUubGY4x3aJPY8nk", + "76a9148b0c5f9ee714e0d1d24642ad63d9d5f398d9b56588ac", { - "isPrivkey": false, - "chain": "test" + "chain": "test", + "isPrivkey": false } ], [ - "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", - "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", + "2N5ymzzKpx6EdUR4UdMZ7t9hcuwqtpHwgw5", + "a9148badb3c3b5c0d39f906f7618e0018b7eae4baf7387", { - "isCompressed": false, - "isPrivkey": true, - "chain": "main" + "chain": "test", + "isPrivkey": false } ], [ - "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", - "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", + "myXnpYbub28zgiJupDdZSWZtDbjcyfJVby", + "76a914c59ac57661b57daadd7c0caf7318c14f54c6c0fa88ac", { - "isCompressed": true, - "isPrivkey": true, - "chain": "main" + "chain": "signet", + "isPrivkey": false } ], [ - "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", - "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "2MtLg8jS5jSXm9evMzTtvpLjy26dBmjFEoT", + "a9140c0007e89cea625d3bf9543baa5a470bb7e5b67287", { - "isCompressed": false, - "isPrivkey": true, - "chain": "test" + "chain": "signet", + "isPrivkey": false } ], [ - "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", - "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "mzCyqdf2UNGdpgkD9NBgLcxdwXRg1i9buY", + "76a914cd04311bdd1ef9c5c24e41930e032aade82a863a88ac", { - "isCompressed": false, - "isPrivkey": true, - "chain": "regtest" + "chain": "regtest", + "isPrivkey": false } ], [ - "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", - "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", + "2N3zGiwFku2vQjYnAqXv5Qu2ztfYRhh7tbF", + "a91475d56d75c88e704d6c72fbe84ac1505abf736b4087", { - "isCompressed": true, - "isPrivkey": true, - "chain": "test" + "chain": "regtest", + "isPrivkey": false } ], [ - "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", - "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac", + "5JUHCgyxNSHg64wwju72eNsG6ajqo4Z2fHHw9iLDLfh69rSiL7w", + "5644d06d88855dacf3192a31df8f4acd8e4c155c52a86d2c1fa48303f5cff053", { - "isPrivkey": false, - "chain": "main" + "chain": "main", + "isCompressed": false, + "isPrivkey": true } ], [ - "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", - "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987", + "L2kZaexG69VSriMe9T2m1jkS86iPe3xNbjcdfakRC1PHe7ay78Ji", + "a50ee94aefcabf5a5d7c85be5b3844dee03c5604861dbfc77fe388c91e5a30f8", { - "isPrivkey": false, - "chain": "main" + "chain": "main", + "isCompressed": true, + "isPrivkey": true } ], [ - "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", - "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac", + "927JwT1ViCr5TD2ZX8CsMNhg17dXmou5xu4y2KiH54zD7i34UJq", + "4502a54c0026b0150281d41f40860d1e23870c63cdc32645bbed688f2ee41f64", { - "isPrivkey": false, - "chain": "test" + "chain": "test", + "isCompressed": false, + "isPrivkey": true } ], [ - "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", - "a914e930e1834a4d234702773951d627cce82fbb5d2e87", + "cTpGGNPVy2Eagawohbr4aGtRJzpLnjxGsGYh9DUcBM45f3KdKGF6", + "ba005a0cb39587aab00bd54c848b59e8adaed47403228567ddc739c2a344ff59", { - "isPrivkey": false, - "chain": "test" + "chain": "test", + "isCompressed": true, + "isPrivkey": true } ], [ - "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", - "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", + "932PLCLA19yPNqV67qwHBSGjxi82LVzWBF7josL9ab4Q1kxgPGF", + "bd8677e076eb39770bf7e9f9e8d3f2cf257effab9b4c220fd3439ccfc208c984", { + "chain": "signet", "isCompressed": false, - "isPrivkey": true, - "chain": "main" + "isPrivkey": true } ], [ - "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", - "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", + "cViUpEy8URSsLjUvxwL7cEuNgCVqM7oKBzd1ZPbA4khcQsQJuj1j", + "f2b36ade8393e29dc71e52cb75ba1109ba210203cd7d0a5ae881ad6846516203", { + "chain": "signet", "isCompressed": true, - "isPrivkey": true, - "chain": "main" + "isPrivkey": true } ], [ - "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", - "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + "92jddDjJCVDmJtgvBHQ9i58PMash8kwsYhRdNo22ea2MYPXdCBE", + "977bf8686f1bcad28f86c4c14afbd33215746bd19203647bf7ff9c6fddc9cc87", { + "chain": "regtest", "isCompressed": false, - "isPrivkey": true, - "chain": "test" + "isPrivkey": true } ], [ - "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", - "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", + "cVwAuMoUqRo399X7vXzuzQyPEvZJMXM8c82zHzRkFCxPCSGx8A6y", + "f93acbbce02b8cb9ddca3fad495441e324cc01eb640b0a7b4c9f0e31644c822a", { + "chain": "regtest", "isCompressed": true, - "isPrivkey": true, - "chain": "test" + "isPrivkey": true } ], [ - "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", - "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac", + "bc1qz377zwe5awr68dnggengqx9vrjt05k98q3sw2n", + "0014147de13b34eb87a3b66846668018ac1c96fa58a7", { + "chain": "main", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", - "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87", + "bc1qkmhskpdzg8kdkfywhu09kswwn9qan9vnkrf6mk40jvnr06s6sz5ssf82ya", + "0020b6ef0b05a241ecdb248ebf1e5b41ce9941d99593b0d3addaaf932637ea1a80a9", { + "chain": "main", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", - "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac", + "bc1ps8cndas60cntk8x79sg9f5e5jz7x050z8agyugln2ukkks23rryqpejzkc", + "512081f136f61a7e26bb1cde2c1054d33490bc67d1e23f504e23f3572d6b415118c8", { + "chain": "main", "isPrivkey": false, - "chain": "test" + "tryCaseFlip": true } ], [ - "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", - "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87", + "bc1zn4tsczge9l", + "52029d57", { + "chain": "main", "isPrivkey": false, - "chain": "test" + "tryCaseFlip": true } ], [ - "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", - "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", + "tb1q6xw0wwd9n9d7ge87dryz4vm5vtahzhvz6yett3", + "0014d19cf739a5995be464fe68c82ab37462fb715d82", { - "isCompressed": false, - "isPrivkey": true, - "chain": "main" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", - "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", + "tb1qwn9zq9fu5uk35ykpgsc7rz4uawy4yh0r5m5er26768h5ur50su3qj6evun", + "002074ca20153ca72d1a12c14431e18abceb89525de3a6e991ab5ed1ef4e0e8f8722", { - "isCompressed": true, - "isPrivkey": true, - "chain": "main" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", - "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + "tb1pmcdc5d8gr92rtemfsnhpeqanvs0nr82upn5dktxluz9n0qcv34lqxke0wq", + "5120de1b8a34e8195435e76984ee1c83b3641f319d5c0ce8db2cdfe08b37830c8d7e", { - "isCompressed": false, - "isPrivkey": true, - "chain": "test" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", - "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + "tb1rgxjvtfzp0xczz6dlzqv8d5cmuykk4qkk", + "531041a4c5a44179b02169bf101876d31be1", { - "isCompressed": true, - "isPrivkey": true, - "chain": "test" + "chain": "test", + "isPrivkey": false, + "tryCaseFlip": true } ], [ - "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", - "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac", + "tb1qa9dlyt6fydestul4y4wh72yshh044w32np8etk", + "0014e95bf22f49237305f3f5255d7f2890bddf5aba2a", { + "chain": "signet", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", - "a9145ece0cadddc415b1980f001785947120acdb36fc87", + "tb1qu4p26n0033720xm0rjgkds5ehdwf039k2fgv75um5krrvfhrrj7qckl9r2", + "0020e542ad4def8c7ca79b6f1c9166c299bb5c97c4b65250cf539ba5863626e31cbc", { + "chain": "signet", "isPrivkey": false, - "chain": "main" + "tryCaseFlip": true } ], [ - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", - "0014751e76e8199196d454941c45d1b3a323f1433bd6", + "tb1pjyukm4n4flwd0ey3lrl06c9kalr60ggmlkcxq2rhhxmy4lvkmkpqexdzqy", + "512091396dd6754fdcd7e491f8fefd60b6efc7a7a11bfdb0602877b9b64afd96dd82", { + "chain": "signet", "isPrivkey": false, - "chain": "main", "tryCaseFlip": true } ], [ - "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080", - "0014751e76e8199196d454941c45d1b3a323f1433bd6", + "tb1r4k75s5syvewsvxufdc3xfhf4tw4u30alw39xny3dnxrl6hau7systymfdv", + "5320adbd485204665d061b896e2264dd355babc8bfbf744a69922d9987fd5fbcf409", { + "chain": "signet", "isPrivkey": false, - "chain": "regtest", "tryCaseFlip": true } ], [ - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", - "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + "bcrt1qnk3tdwwj47ppc4pqmxkjdusegedn9ru5gvccwa", + "00149da2b6b9d2af821c5420d9ad26f219465b328f94", { + "chain": "regtest", "isPrivkey": false, - "chain": "test", "tryCaseFlip": true } ], [ - "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", - "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", + "bcrt1qz7prfshfkwsxuk72pt6mzr6uumq4qllxe4vmwqt89tat48d362yqlykk6a", + "0020178234c2e9b3a06e5bca0af5b10f5ce6c1507fe6cd59b701672afaba9db1d288", { + "chain": "regtest", "isPrivkey": false, - "chain": "main", "tryCaseFlip": true } ], [ - "bc1sw50qa3jx3s", - "6002751e", + "bcrt1pumee3wj80xvyr7wjmj7zsk26x5pn095aegy862yhx6f2j9sgc9hq6cj4cm", + "5120e6f398ba47799841f9d2dcbc28595a350337969dca087d28973692a91608c16e", { + "chain": "regtest", "isPrivkey": false, - "chain": "main", "tryCaseFlip": true } ], [ - "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", - "5210751e76e8199196d454941c45d1b3a323", + "bcrt1szqz8hj64d2hhc6nt65v09jxal66pgff2xpcp9kj648qkk8kjzxelsts4dktd799g47uase", + "602810047bcb556aaf7c6a6bd518f2c8ddfeb414252a307012da5aa9c16b1ed211b3f82e156d96df14a8", { + "chain": "regtest", "isPrivkey": false, - "chain": "main", "tryCaseFlip": true } ], [ - "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", - "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + "12agZTajtRE3STSchwWNWnrm467zzTQ916", + "76a9141156e00f70061e5faba8b71593a8c7554b47090c88ac", + { + "chain": "main", + "isPrivkey": false + } + ], + [ + "3NXqB6iZiPYbKruNT3d9xNBTmtb73xMvvf", + "a914e49decc9e5d97e0547d3642f3a4795b13ae62bca87", + { + "chain": "main", + "isPrivkey": false + } + ], + [ + "mjgt4BoCYxjzWvJFoh68x7cj5GeaKDYhyx", + "76a9142dc11fc7b8072f733f690ffb0591c00f4062295c88ac", { - "isPrivkey": false, "chain": "test", - "tryCaseFlip": true + "isPrivkey": false } ], [ - "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7", - "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + "2NCT6FdQ5MxorHgnFxLeHyGwTGRdkHcrJDH", + "a914d2a8ec992b0894a0d9391ca5d9c45c388c41be7e87", { - "isPrivkey": false, - "chain": "regtest", - "tryCaseFlip": true + "chain": "test", + "isPrivkey": false + } + ], + [ + "mpomiA7wqDnMcxaNLC23eBuXAb4U6H4ZqW", + "76a91465e75e340415ed297c58d6a14d3c17ceeaa17bbd88ac", + { + "chain": "signet", + "isPrivkey": false + } + ], + [ + "2N1pGAA5uatbU2PKvMA9BnJmHcK6yHfMiZa", + "a9145e008b6cc232164570befc23d216060bf4ea793b87", + { + "chain": "signet", + "isPrivkey": false } ] ] diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index acbd6a01ee..aecf955fee 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -10,6 +10,7 @@ #include <boost/test/unit_test.hpp> +#include <optional> #include <string> #include <vector> @@ -65,7 +66,7 @@ std::string UseHInsteadOfApostrophe(const std::string& desc) const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}}; -void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const Optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, +void DoCheck(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false) { FlatSigningProvider keys_priv, keys_pub; @@ -262,7 +263,7 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string& BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv); } -void Check(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const Optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY) +void Check(const std::string& prv, const std::string& pub, const std::string& norm_prv, const std::string& norm_pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY) { bool found_apostrophes_in_prv = false; bool found_apostrophes_in_pub = false; @@ -295,8 +296,8 @@ BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(descriptor_test) { // Basic single-key compressed - Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, nullopt); - Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, nullopt); + Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt); + Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt); Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}}); Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32); Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT); @@ -305,8 +306,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin // Basic single-key uncompressed - Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, nullopt); - Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, nullopt); + Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt); + Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt); Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY); CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Uncompressed keys are not allowed"); // No uncompressed keys in witness CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "Uncompressed keys are not allowed"); // No uncompressed keys in witness @@ -321,23 +322,23 @@ BOOST_AUTO_TEST_CASE(descriptor_test) Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}}, OutputType::P2SH_SEGWIT); // Versions with BIP32 derivations - Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, nullopt); - Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, nullopt, {{0}}); + Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt); + Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, {{0}}); Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0)", "pkh([bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}}); Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}}); Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); - Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, nullopt, {{0}, {1}}); + Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}}); CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "Key path value 2147483648 is out of range"); // BIP 32 path element overflow CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "Key path value '1aa' is not a valid uint32"); // Path is not valid uint Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0)", "pkh([01234567/10/20/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}}); // Multisig constructions - Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt); - Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt); - Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, nullopt); + Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); + Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); + Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); - Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}}); + Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}}); Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xprv9vHkqa6XAPwKqSKSEJMcAB3yoCZhaSVsGZbSkFY5L3Lfjjk8sjZucbsbvEw5o3QrSA69nPfZDCgFnNnLhQ2ohpZuwummndnPasDw2Qr6dC2/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647']xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT); CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index b55f1c72b1..0baf30aef6 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -104,7 +104,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman) [&] { const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); if (opt_service) { - addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()}); + addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); } }, [&] { diff --git a/src/test/fuzz/asmap_direct.cpp b/src/test/fuzz/asmap_direct.cpp index 8b7822dc16..8ca4de3919 100644 --- a/src/test/fuzz/asmap_direct.cpp +++ b/src/test/fuzz/asmap_direct.cpp @@ -2,8 +2,9 @@ // 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 <netaddress.h> #include <util/asmap.h> +#include <test/fuzz/fuzz.h> #include <cstdint> #include <optional> diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp index dbc0b5ab81..479342e4be 100644 --- a/src/test/fuzz/autofile.cpp +++ b/src/test/fuzz/autofile.cpp @@ -2,7 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <optional.h> #include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> diff --git a/src/test/fuzz/bech32.cpp b/src/test/fuzz/bech32.cpp index 95cd4b413f..ad3bf73af4 100644 --- a/src/test/fuzz/bech32.cpp +++ b/src/test/fuzz/bech32.cpp @@ -16,28 +16,28 @@ FUZZ_TARGET(bech32) { const std::string random_string(buffer.begin(), buffer.end()); - const std::pair<std::string, std::vector<uint8_t>> r1 = bech32::Decode(random_string); - if (r1.first.empty()) { - assert(r1.second.empty()); + const auto r1 = bech32::Decode(random_string); + if (r1.hrp.empty()) { + assert(r1.encoding == bech32::Encoding::INVALID); + assert(r1.data.empty()); } else { - const std::string& hrp = r1.first; - const std::vector<uint8_t>& data = r1.second; - const std::string reencoded = bech32::Encode(hrp, data); + assert(r1.encoding != bech32::Encoding::INVALID); + const std::string reencoded = bech32::Encode(r1.encoding, r1.hrp, r1.data); assert(CaseInsensitiveEqual(random_string, reencoded)); } std::vector<unsigned char> input; ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end()); - const std::string encoded = bech32::Encode("bc", input); - assert(!encoded.empty()); - const std::pair<std::string, std::vector<uint8_t>> r2 = bech32::Decode(encoded); - if (r2.first.empty()) { - assert(r2.second.empty()); - } else { - const std::string& hrp = r2.first; - const std::vector<uint8_t>& data = r2.second; - assert(hrp == "bc"); - assert(data == input); + if (input.size() + 3 + 6 <= 90) { + // If it's possible to encode input in Bech32(m) without exceeding the 90-character limit: + for (auto encoding : {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) { + const std::string encoded = bech32::Encode(encoding, "bc", input); + assert(!encoded.empty()); + const auto r2 = bech32::Decode(encoded); + assert(r2.encoding == encoding); + assert(r2.hrp == "bc"); + assert(r2.data == input); + } } } diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp index ffe38f10fc..ed72260d10 100644 --- a/src/test/fuzz/buffered_file.cpp +++ b/src/test/fuzz/buffered_file.cpp @@ -2,7 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <optional.h> #include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> diff --git a/src/test/fuzz/cuckoocache.cpp b/src/test/fuzz/cuckoocache.cpp index dc20dc3f62..a522c837ef 100644 --- a/src/test/fuzz/cuckoocache.cpp +++ b/src/test/fuzz/cuckoocache.cpp @@ -30,7 +30,7 @@ FUZZ_TARGET(cuckoocache) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); fuzzed_data_provider_ptr = &fuzzed_data_provider; - CuckooCache::cache<bool, RandomHasher> cuckoo_cache{}; + CuckooCache::cache<int, RandomHasher> cuckoo_cache{}; if (fuzzed_data_provider.ConsumeBool()) { const size_t megabytes = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 16); cuckoo_cache.setup_bytes(megabytes << 20); diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp index 64c6e49615..1290c78712 100644 --- a/src/test/fuzz/deserialize.cpp +++ b/src/test/fuzz/deserialize.cpp @@ -15,7 +15,6 @@ #include <net.h> #include <netbase.h> #include <node/utxo_snapshot.h> -#include <optional.h> #include <primitives/block.h> #include <protocol.h> #include <psbt.h> @@ -26,6 +25,7 @@ #include <version.h> #include <exception> +#include <optional> #include <stdexcept> #include <stdint.h> #include <unistd.h> @@ -69,7 +69,7 @@ T Deserialize(CDataStream ds) } template <typename T> -void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const Optional<int> protocol_version = nullopt) +void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); if (protocol_version) { diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index b056f46f2e..272f6415a9 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -7,7 +7,6 @@ #include <net.h> #include <net_permissions.h> #include <netaddress.h> -#include <optional.h> #include <protocol.h> #include <random.h> #include <test/fuzz/FuzzedDataProvider.h> @@ -17,6 +16,7 @@ #include <test/util/setup_common.h> #include <cstdint> +#include <optional> #include <string> #include <vector> diff --git a/src/test/fuzz/netbase_dns_lookup.cpp b/src/test/fuzz/netbase_dns_lookup.cpp index 786440c8a4..cf2fa33744 100644 --- a/src/test/fuzz/netbase_dns_lookup.cpp +++ b/src/test/fuzz/netbase_dns_lookup.cpp @@ -12,27 +12,22 @@ #include <string> #include <vector> -namespace { -FuzzedDataProvider* fuzzed_data_provider_ptr = nullptr; - -std::vector<CNetAddr> fuzzed_dns_lookup_function(const std::string& name, bool allow_lookup) -{ - std::vector<CNetAddr> resolved_addresses; - while (fuzzed_data_provider_ptr->ConsumeBool()) { - resolved_addresses.push_back(ConsumeNetAddr(*fuzzed_data_provider_ptr)); - } - return resolved_addresses; -} -} // namespace - FUZZ_TARGET(netbase_dns_lookup) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - fuzzed_data_provider_ptr = &fuzzed_data_provider; const std::string name = fuzzed_data_provider.ConsumeRandomLengthString(512); const unsigned int max_results = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); const bool allow_lookup = fuzzed_data_provider.ConsumeBool(); - const int default_port = fuzzed_data_provider.ConsumeIntegral<int>(); + const uint16_t default_port = fuzzed_data_provider.ConsumeIntegral<uint16_t>(); + + auto fuzzed_dns_lookup_function = [&](const std::string&, bool) { + std::vector<CNetAddr> resolved_addresses; + while (fuzzed_data_provider.ConsumeBool()) { + resolved_addresses.push_back(ConsumeNetAddr(fuzzed_data_provider)); + } + return resolved_addresses; + }; + { std::vector<CNetAddr> resolved_addresses; if (LookupHost(name, resolved_addresses, max_results, allow_lookup, fuzzed_dns_lookup_function)) { @@ -73,5 +68,4 @@ FUZZ_TARGET(netbase_dns_lookup) assert(resolved_subnet.IsValid()); } } - fuzzed_data_provider_ptr = nullptr; } diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp index 606ebfc151..603d520cf5 100644 --- a/src/test/fuzz/node_eviction.cpp +++ b/src/test/fuzz/node_eviction.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <net.h> -#include <optional.h> #include <protocol.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> @@ -37,7 +36,7 @@ FUZZ_TARGET(node_eviction) // Make a copy since eviction_candidates may be in some valid but otherwise // indeterminate state after the SelectNodeToEvict(&&) call. const std::vector<NodeEvictionCandidate> eviction_candidates_copy = eviction_candidates; - const Optional<NodeId> node_to_evict = SelectNodeToEvict(std::move(eviction_candidates)); + const std::optional<NodeId> node_to_evict = SelectNodeToEvict(std::move(eviction_candidates)); if (node_to_evict) { assert(std::any_of(eviction_candidates_copy.begin(), eviction_candidates_copy.end(), [&node_to_evict](const NodeEvictionCandidate& eviction_candidate) { return *node_to_evict == eviction_candidate.id; })); } diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp index 163f1b839e..3a1fdaad8f 100644 --- a/src/test/fuzz/p2p_transport_deserializer.cpp +++ b/src/test/fuzz/p2p_transport_deserializer.cpp @@ -10,6 +10,7 @@ #include <cassert> #include <cstdint> #include <limits> +#include <optional> #include <vector> void initialize_p2p_transport_deserializer() @@ -30,7 +31,7 @@ FUZZ_TARGET_INIT(p2p_transport_deserializer, initialize_p2p_transport_deserializ if (deserializer.Complete()) { const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()}; uint32_t out_err_raw_size{0}; - Optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)}; + std::optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)}; if (result) { assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE); assert(result->m_raw_message_size <= buffer.size()); diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp index ddd2bcfba3..2c546e9b4a 100644 --- a/src/test/fuzz/parse_numbers.cpp +++ b/src/test/fuzz/parse_numbers.cpp @@ -18,6 +18,12 @@ FUZZ_TARGET(parse_numbers) double d; (void)ParseDouble(random_string, &d); + uint8_t u8; + (void)ParseUInt8(random_string, &u8); + + uint16_t u16; + (void)ParseUInt16(random_string, &u16); + int32_t i32; (void)ParseInt32(random_string, &i32); (void)atoi(random_string); diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp index eb2754054a..d1cc6f9c7e 100644 --- a/src/test/fuzz/psbt.cpp +++ b/src/test/fuzz/psbt.cpp @@ -5,7 +5,6 @@ #include <test/fuzz/fuzz.h> #include <node/psbt.h> -#include <optional.h> #include <psbt.h> #include <pubkey.h> #include <script/script.h> @@ -13,6 +12,7 @@ #include <version.h> #include <cstdint> +#include <optional> #include <string> #include <vector> @@ -39,7 +39,7 @@ FUZZ_TARGET_INIT(psbt, initialize_psbt) (void)psbt.IsNull(); - Optional<CMutableTransaction> tx = psbt.tx; + std::optional<CMutableTransaction> tx = psbt.tx; if (tx) { const CMutableTransaction& mtx = *tx; const PartiallySignedTransaction psbt_from_tx{mtx}; diff --git a/src/test/fuzz/script_descriptor_cache.cpp b/src/test/fuzz/script_descriptor_cache.cpp index 1c62c018e7..6ce13d5679 100644 --- a/src/test/fuzz/script_descriptor_cache.cpp +++ b/src/test/fuzz/script_descriptor_cache.cpp @@ -2,7 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <optional.h> #include <pubkey.h> #include <script/descriptor.h> #include <test/fuzz/FuzzedDataProvider.h> @@ -10,6 +9,7 @@ #include <test/fuzz/util.h> #include <cstdint> +#include <optional> #include <string> #include <vector> diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp index e5cc4cabe5..c3a6eed089 100644 --- a/src/test/fuzz/socks5.cpp +++ b/src/test/fuzz/socks5.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 <netaddress.h> #include <netbase.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> @@ -38,7 +39,7 @@ FUZZ_TARGET_INIT(socks5, initialize_socks5) // This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within // a few seconds of fuzzing. (void)Socks5(fuzzed_data_provider.ConsumeRandomLengthString(512), - fuzzed_data_provider.ConsumeIntegral<int>(), + fuzzed_data_provider.ConsumeIntegral<uint16_t>(), fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr, fuzzed_sock); } diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp index 93b4948a2f..286375f7ae 100644 --- a/src/test/fuzz/string.cpp +++ b/src/test/fuzz/string.cpp @@ -5,6 +5,7 @@ #include <blockfilter.h> #include <clientversion.h> #include <logging.h> +#include <netaddress.h> #include <netbase.h> #include <outputtype.h> #include <rpc/client.h> @@ -82,7 +83,7 @@ FUZZ_TARGET(string) #ifndef WIN32 (void)ShellEscape(random_string_1); #endif // WIN32 - int port_out; + uint16_t port_out; std::string host_out; SplitHostPort(random_string_1, port_out, host_out); (void)TimingResistantEqual(random_string_1, random_string_2); diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index d9571209fa..b25dcfcd3b 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -51,7 +51,7 @@ FUZZ_TARGET(system) // Avoid hitting: // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16)); - if (args_manager.GetArgFlags(argument_name) != nullopt) { + if (args_manager.GetArgFlags(argument_name) != std::nullopt) { return; } args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>() & ~ArgsManager::COMMAND, options_category); @@ -63,7 +63,7 @@ FUZZ_TARGET(system) std::vector<std::string> hidden_arguments; for (const std::string& name : names) { const std::string hidden_argument = GetArgumentName(name); - if (args_manager.GetArgFlags(hidden_argument) != nullopt) { + if (args_manager.GetArgFlags(hidden_argument) != std::nullopt) { continue; } if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) { diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp new file mode 100644 index 0000000000..2a9363a7c3 --- /dev/null +++ b/src/test/fuzz/tx_pool.cpp @@ -0,0 +1,284 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <test/util/mining.h> +#include <test/util/script.h> +#include <test/util/setup_common.h> +#include <util/rbf.h> +#include <validation.h> +#include <validationinterface.h> + +namespace { + +const TestingSetup* g_setup; +std::vector<COutPoint> g_outpoints_coinbase_init; + +struct MockedTxPool : public CTxMemPool { + void RollingFeeUpdate() + { + lastRollingFeeUpdate = GetTime(); + blockSinceLastRollingFeeBump = true; + } +}; + +void initialize_tx_pool() +{ + static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); + g_setup = testing_setup.get(); + + for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) { + CTxIn in = MineBlock(g_setup->m_node, P2WSH_OP_TRUE); + // Remember the txids to avoid expensive disk acess later on + g_outpoints_coinbase_init.push_back(in.prevout); + } + SyncWithValidationInterfaceQueue(); +} + +struct TransactionsDelta final : public CValidationInterface { + std::set<CTransactionRef>& m_removed; + std::set<CTransactionRef>& m_added; + + explicit TransactionsDelta(std::set<CTransactionRef>& r, std::set<CTransactionRef>& a) + : m_removed{r}, m_added{a} {} + + void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t /* mempool_sequence */) override + { + Assert(m_added.insert(tx).second); + } + + void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override + { + Assert(m_removed.insert(tx).second); + } +}; + +void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider) +{ + args.ForceSetArg("-limitancestorcount", + ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50))); + args.ForceSetArg("-limitancestorsize", + ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202))); + args.ForceSetArg("-limitdescendantcount", + ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50))); + args.ForceSetArg("-limitdescendantsize", + ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202))); + args.ForceSetArg("-maxmempool", + ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200))); + args.ForceSetArg("-mempoolexpiry", + ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999))); +} + +FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const auto& node = g_setup->m_node; + auto& chainstate = node.chainman->ActiveChainstate(); + + SetMockTime(ConsumeTime(fuzzed_data_provider)); + SetMempoolConstraints(*node.args, fuzzed_data_provider); + + // All RBF-spendable outpoints + std::set<COutPoint> outpoints_rbf; + // All outpoints counting toward the total supply (subset of outpoints_rbf) + std::set<COutPoint> outpoints_supply; + for (const auto& outpoint : g_outpoints_coinbase_init) { + Assert(outpoints_supply.insert(outpoint).second); + if (outpoints_supply.size() >= COINBASE_MATURITY) break; + } + outpoints_rbf = outpoints_supply; + + // The sum of the values of all spendable outpoints + constexpr CAmount SUPPLY_TOTAL{COINBASE_MATURITY * 50 * COIN}; + + CTxMemPool tx_pool_{/* estimator */ nullptr, /* check_ratio */ 1}; + MockedTxPool& tx_pool = *(MockedTxPool*)&tx_pool_; + + // Helper to query an amount + const CCoinsViewMemPool amount_view{WITH_LOCK(::cs_main, return &chainstate.CoinsTip()), tx_pool}; + const auto GetAmount = [&](const COutPoint& outpoint) { + Coin c; + amount_view.GetCoin(outpoint, c); + Assert(!c.IsSpent()); + return c.out.nValue; + }; + + while (fuzzed_data_provider.ConsumeBool()) { + { + // Total supply is the mempool fee + all outpoints + CAmount supply_now{WITH_LOCK(tx_pool.cs, return tx_pool.GetTotalFee())}; + for (const auto& op : outpoints_supply) { + supply_now += GetAmount(op); + } + Assert(supply_now == SUPPLY_TOTAL); + } + Assert(!outpoints_supply.empty()); + + // Create transaction to add to the mempool + const CTransactionRef tx = [&] { + CMutableTransaction tx_mut; + tx_mut.nVersion = CTransaction::CURRENT_VERSION; + tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, outpoints_rbf.size()); + const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, outpoints_rbf.size() * 2); + + CAmount amount_in{0}; + for (int i = 0; i < num_in; ++i) { + // Pop random outpoint + auto pop = outpoints_rbf.begin(); + std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints_rbf.size() - 1)); + const auto outpoint = *pop; + outpoints_rbf.erase(pop); + amount_in += GetAmount(outpoint); + + // Create input + const auto sequence = ConsumeSequence(fuzzed_data_provider); + const auto script_sig = CScript{}; + const auto script_wit_stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE}; + CTxIn in; + in.prevout = outpoint; + in.nSequence = sequence; + in.scriptSig = script_sig; + in.scriptWitness.stack = script_wit_stack; + + tx_mut.vin.push_back(in); + } + const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1000, amount_in); + const auto amount_out = (amount_in - amount_fee) / num_out; + for (int i = 0; i < num_out; ++i) { + tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE); + } + const auto tx = MakeTransactionRef(tx_mut); + // Restore previously removed outpoints + for (const auto& in : tx->vin) { + Assert(outpoints_rbf.insert(in.prevout).second); + } + return tx; + }(); + + if (fuzzed_data_provider.ConsumeBool()) { + SetMockTime(ConsumeTime(fuzzed_data_provider)); + } + if (fuzzed_data_provider.ConsumeBool()) { + SetMempoolConstraints(*node.args, fuzzed_data_provider); + } + if (fuzzed_data_provider.ConsumeBool()) { + tx_pool.RollingFeeUpdate(); + } + if (fuzzed_data_provider.ConsumeBool()) { + const auto& txid = fuzzed_data_provider.ConsumeBool() ? + tx->GetHash() : + PickValue(fuzzed_data_provider, outpoints_rbf).hash; + const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN); + tx_pool.PrioritiseTransaction(txid, delta); + } + + // Remember all removed and added transactions + std::set<CTransactionRef> removed; + std::set<CTransactionRef> added; + auto txr = std::make_shared<TransactionsDelta>(removed, added); + RegisterSharedValidationInterface(txr); + const bool bypass_limits = fuzzed_data_provider.ConsumeBool(); + ::fRequireStandard = fuzzed_data_provider.ConsumeBool(); + const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx_pool, tx, bypass_limits)); + const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; + SyncWithValidationInterfaceQueue(); + UnregisterSharedValidationInterface(txr); + + Assert(accepted != added.empty()); + Assert(accepted == res.m_state.IsValid()); + Assert(accepted != res.m_state.IsInvalid()); + if (accepted) { + Assert(added.size() == 1); // For now, no package acceptance + Assert(tx == *added.begin()); + } else { + // Do not consider rejected transaction removed + removed.erase(tx); + } + + // Helper to insert spent and created outpoints of a tx into collections + using Sets = std::vector<std::reference_wrapper<std::set<COutPoint>>>; + const auto insert_tx = [](Sets created_by_tx, Sets consumed_by_tx, const auto& tx) { + for (size_t i{0}; i < tx.vout.size(); ++i) { + for (auto& set : created_by_tx) { + Assert(set.get().emplace(tx.GetHash(), i).second); + } + } + for (const auto& in : tx.vin) { + for (auto& set : consumed_by_tx) { + Assert(set.get().insert(in.prevout).second); + } + } + }; + // Add created outpoints, remove spent outpoints + { + // Outpoints that no longer exist at all + std::set<COutPoint> consumed_erased; + // Outpoints that no longer count toward the total supply + std::set<COutPoint> consumed_supply; + for (const auto& removed_tx : removed) { + insert_tx(/* created_by_tx */ {consumed_erased}, /* consumed_by_tx */ {outpoints_supply}, /* tx */ *removed_tx); + } + for (const auto& added_tx : added) { + insert_tx(/* created_by_tx */ {outpoints_supply, outpoints_rbf}, /* consumed_by_tx */ {consumed_supply}, /* tx */ *added_tx); + } + for (const auto& p : consumed_erased) { + Assert(outpoints_supply.erase(p) == 1); + Assert(outpoints_rbf.erase(p) == 1); + } + for (const auto& p : consumed_supply) { + Assert(outpoints_supply.erase(p) == 1); + } + } + } + WITH_LOCK(::cs_main, tx_pool.check(chainstate)); + const auto info_all = tx_pool.infoAll(); + if (!info_all.empty()) { + const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx; + WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, /* dummy */ MemPoolRemovalReason::BLOCK)); + std::vector<uint256> all_txids; + tx_pool.queryHashes(all_txids); + assert(all_txids.size() < info_all.size()); + WITH_LOCK(::cs_main, tx_pool.check(chainstate)); + } + SyncWithValidationInterfaceQueue(); +} + +FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const auto& node = g_setup->m_node; + + std::vector<uint256> txids; + for (const auto& outpoint : g_outpoints_coinbase_init) { + txids.push_back(outpoint.hash); + if (txids.size() >= COINBASE_MATURITY) break; + } + for (int i{0}; i <= 3; ++i) { + // Add some immature and non-existent outpoints + txids.push_back(g_outpoints_coinbase_init.at(i).hash); + txids.push_back(ConsumeUInt256(fuzzed_data_provider)); + } + + CTxMemPool tx_pool{/* estimator */ nullptr, /* check_ratio */ 1}; + + while (fuzzed_data_provider.ConsumeBool()) { + const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids); + + const auto tx = MakeTransactionRef(mut_tx); + const bool bypass_limits = fuzzed_data_provider.ConsumeBool(); + ::fRequireStandard = fuzzed_data_provider.ConsumeBool(); + const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(node.chainman->ActiveChainstate(), tx_pool, tx, bypass_limits)); + const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; + if (accepted) { + txids.push_back(tx->GetHash()); + } + + SyncWithValidationInterfaceQueue(); + } +} +} // namespace diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index 0a541e4186..93418ab1ff 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -3,8 +3,11 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <test/fuzz/util.h> +#include <test/util/script.h> +#include <util/rbf.h> #include <version.h> + void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept { const ServiceFlags remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); @@ -23,3 +26,78 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_v node.m_tx_relay->fRelayTxes = filter_txs; } } + +CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in, const int max_num_out) noexcept +{ + CMutableTransaction tx_mut; + const auto p2wsh_op_true = fuzzed_data_provider.ConsumeBool(); + tx_mut.nVersion = fuzzed_data_provider.ConsumeBool() ? + CTransaction::CURRENT_VERSION : + fuzzed_data_provider.ConsumeIntegral<int32_t>(); + tx_mut.nLockTime = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_in); + const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_out); + for (int i = 0; i < num_in; ++i) { + const auto& txid_prev = prevout_txids ? + PickValue(fuzzed_data_provider, *prevout_txids) : + ConsumeUInt256(fuzzed_data_provider); + const auto index_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, max_num_out); + const auto sequence = ConsumeSequence(fuzzed_data_provider); + const auto script_sig = p2wsh_op_true ? CScript{} : ConsumeScript(fuzzed_data_provider); + CScriptWitness script_wit; + if (p2wsh_op_true) { + script_wit.stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE}; + } else { + script_wit = ConsumeScriptWitness(fuzzed_data_provider); + } + CTxIn in; + in.prevout = COutPoint{txid_prev, index_out}; + in.nSequence = sequence; + in.scriptSig = script_sig; + in.scriptWitness = script_wit; + + tx_mut.vin.push_back(in); + } + for (int i = 0; i < num_out; ++i) { + const auto amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-10, 50 * COIN + 10); + const auto script_pk = p2wsh_op_true ? + P2WSH_OP_TRUE : + ConsumeScript(fuzzed_data_provider, /* max_length */ 128, /* maybe_p2wsh */ true); + tx_mut.vout.emplace_back(amount, script_pk); + } + return tx_mut; +} + +CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size) noexcept +{ + CScriptWitness ret; + const auto n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_stack_elem_size); + for (size_t i = 0; i < n_elements; ++i) { + ret.stack.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider)); + } + return ret; +} + +CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length, const bool maybe_p2wsh) noexcept +{ + const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + CScript r_script{b.begin(), b.end()}; + if (maybe_p2wsh && fuzzed_data_provider.ConsumeBool()) { + uint256 script_hash; + CSHA256().Write(&r_script[0], r_script.size()).Finalize(script_hash.begin()); + r_script.clear(); + r_script << OP_0 << ToByteVector(script_hash); + } + return r_script; +} + +uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + return fuzzed_data_provider.ConsumeBool() ? + fuzzed_data_provider.PickValueInArray({ + CTxIn::SEQUENCE_FINAL, + CTxIn::SEQUENCE_FINAL - 1, + MAX_BIP125_RBF_SEQUENCE, + }) : + fuzzed_data_provider.ConsumeIntegral<uint32_t>(); +} diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index d8c536e8b1..df459fe60d 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -48,6 +48,16 @@ void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables) return ((i++ == call_index ? callables() : void()), ...); } +template <typename Collection> +const auto& PickValue(FuzzedDataProvider& fuzzed_data_provider, const Collection& col) +{ + const auto sz = col.size(); + assert(sz >= 1); + auto it = col.begin(); + std::advance(it, fuzzed_data_provider.ConsumeIntegralInRange<decltype(sz)>(0, sz - 1)); + return *it; +} + [[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length); @@ -125,11 +135,13 @@ template <typename WeakEnumType, size_t size> return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max); } -[[nodiscard]] inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept -{ - const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); - return {b.begin(), b.end()}; -} +[[nodiscard]] CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in = 10, const int max_num_out = 10) noexcept; + +[[nodiscard]] CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size = 32) noexcept; + +[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096, const bool maybe_p2wsh = false) noexcept; + +[[nodiscard]] uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept; [[nodiscard]] inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_provider) noexcept { @@ -259,6 +271,16 @@ void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array<T errno = fuzzed_data_provider.PickValueInArray(errnos); } +/* + * Sets a fuzzed errno in the range [0, 133 (EHWPOISON)]. Can be used from functions emulating + * standard library functions that set errno, or in other contexts where the value of errno + * might be relevant for the execution path that will be taken. + */ +inline void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + errno = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 133); +} + /** * Returns a byte vector of specified size regardless of the number of remaining bytes available * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size. @@ -345,6 +367,7 @@ public: FILE* open() { + SetFuzzedErrNo(m_fuzzed_data_provider); if (m_fuzzed_data_provider.ConsumeBool()) { return nullptr; } @@ -386,6 +409,7 @@ public: static ssize_t read(void* cookie, char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } @@ -404,6 +428,7 @@ public: static ssize_t write(void* cookie, const char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size); if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; @@ -414,8 +439,9 @@ public: static int seek(void* cookie, int64_t* offset, int whence) { - assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet. + assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END); FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); int64_t new_offset = 0; if (whence == SEEK_SET) { new_offset = *offset; @@ -424,6 +450,12 @@ public: return -1; } new_offset = fuzzed_file->m_offset + *offset; + } else if (whence == SEEK_END) { + const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096); + if (AdditionOverflow(n, *offset)) { + return -1; + } + new_offset = n + *offset; } if (new_offset < 0) { return -1; @@ -436,6 +468,7 @@ public: static int close(void* cookie) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0); } }; diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp new file mode 100644 index 0000000000..e1a21b6c53 --- /dev/null +++ b/src/test/fuzz/validation_load_mempool.cpp @@ -0,0 +1,34 @@ +// 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 <chainparamsbase.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <test/util/setup_common.h> +#include <txmempool.h> +#include <util/time.h> +#include <validation.h> + +#include <cstdint> +#include <vector> + +void initialize_validation_load_mempool() +{ + static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); +} + +FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); + + CTxMemPool pool{}; + auto fuzzed_fopen = [&](const fs::path&, const char*) { + return fuzzed_file_provider.open(); + }; + (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen); + (void)DumpMempool(pool, fuzzed_fopen, true); +} diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp new file mode 100644 index 0000000000..a898e2782d --- /dev/null +++ b/src/test/fuzz/versionbits.cpp @@ -0,0 +1,345 @@ +// Copyright (c) 2020-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <chain.h> +#include <chainparams.h> +#include <consensus/params.h> +#include <primitives/block.h> +#include <versionbits.h> + +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> + +#include <cstdint> +#include <limits> +#include <memory> +#include <vector> + +namespace { +class TestConditionChecker : public AbstractThresholdConditionChecker +{ +private: + mutable ThresholdConditionCache m_cache; + const Consensus::Params dummy_params{}; + +public: + const int64_t m_begin = 0; + const int64_t m_end = 0; + const int m_period = 0; + const int m_threshold = 0; + const int m_bit = 0; + + TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int bit) + : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_bit{bit} + { + assert(m_period > 0); + assert(0 <= m_threshold && m_threshold <= m_period); + assert(0 <= m_bit && m_bit <= 32 && m_bit < VERSIONBITS_NUM_BITS); + } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); } + int64_t BeginTime(const Consensus::Params& params) const override { return m_begin; } + int64_t EndTime(const Consensus::Params& params) const override { return m_end; } + int Period(const Consensus::Params& params) const override { return m_period; } + int Threshold(const Consensus::Params& params) const override { return m_threshold; } + + ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); } + int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); } + BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindexPrev, dummy_params); } + + bool Condition(int64_t version) const + { + return ((version >> m_bit) & 1) != 0 && (version & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS; + } + + bool Condition(const CBlockIndex* pindex) const { return Condition(pindex->nVersion); } +}; + +/** Track blocks mined for test */ +class Blocks +{ +private: + std::vector<std::unique_ptr<CBlockIndex>> m_blocks; + const uint32_t m_start_time; + const uint32_t m_interval; + const int32_t m_signal; + const int32_t m_no_signal; + +public: + Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal) + : m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {} + + size_t size() const { return m_blocks.size(); } + + CBlockIndex* tip() const + { + return m_blocks.empty() ? nullptr : m_blocks.back().get(); + } + + CBlockIndex* mine_block(bool signal) + { + CBlockHeader header; + header.nVersion = signal ? m_signal : m_no_signal; + header.nTime = m_start_time + m_blocks.size() * m_interval; + header.nBits = 0x1d00ffff; + + auto current_block = std::make_unique<CBlockIndex>(header); + current_block->pprev = tip(); + current_block->nHeight = m_blocks.size(); + current_block->BuildSkip(); + + return m_blocks.emplace_back(std::move(current_block)).get(); + } +}; + +void initialize() +{ + SelectParams(CBaseChainParams::MAIN); +} +} // namespace + +constexpr uint32_t MAX_TIME = 4102444800; // 2100-01-01 + +FUZZ_TARGET_INIT(versionbits, initialize) +{ + const CChainParams& params = Params(); + + const int64_t interval = params.GetConsensus().nPowTargetSpacing; + assert(interval > 1); // need to be able to halve it + assert(interval < std::numeric_limits<int32_t>::max()); + + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + + // making period/max_periods larger slows these tests down significantly + const int period = 32; + const size_t max_periods = 16; + const size_t max_blocks = 2 * period * max_periods; + + const int threshold = fuzzed_data_provider.ConsumeIntegralInRange(1, period); + assert(0 < threshold && threshold <= period); // must be able to both pass and fail threshold! + + // too many blocks at 10min each might cause uint32_t time to overflow if + // block_start_time is at the end of the range above + assert(std::numeric_limits<uint32_t>::max() - MAX_TIME > interval * max_blocks); + + const int64_t block_start_time = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(params.GenesisBlock().nTime, MAX_TIME); + + // what values for version will we use to signal / not signal? + const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>(); + const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>(); + + // select deployment parameters: bit, start time, timeout + const int bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1); + + bool always_active_test = false; + bool never_active_test = false; + int64_t start_time; + int64_t timeout; + if (fuzzed_data_provider.ConsumeBool()) { + // pick the timestamp to switch based on a block + // note states will change *after* these blocks because mediantime lags + int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3)); + int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(start_block, period * (max_periods - 3)); + + start_time = block_start_time + start_block * interval; + timeout = block_start_time + end_block * interval; + + assert(start_time <= timeout); + + // allow for times to not exactly match a block + if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2; + if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2; + + // this may make timeout too early; if so, don't run the test + if (start_time > timeout) return; + } else { + if (fuzzed_data_provider.ConsumeBool()) { + start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + timeout = Consensus::BIP9Deployment::NO_TIMEOUT; + always_active_test = true; + } else { + start_time = 1199145601; // January 1, 2008 + timeout = 1230767999; // December 31, 2008 + never_active_test = true; + } + } + + TestConditionChecker checker(start_time, timeout, period, threshold, bit); + + // Early exit if the versions don't signal sensibly for the deployment + if (!checker.Condition(ver_signal)) return; + if (checker.Condition(ver_nosignal)) return; + if (ver_nosignal < 0) return; + + // TOP_BITS should ensure version will be positive + assert(ver_signal > 0); + + // Now that we have chosen time and versions, setup to mine blocks + Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal); + + /* Strategy: + * * we will mine a final period worth of blocks, with + * randomised signalling according to a mask + * * but before we mine those blocks, we will mine some + * randomised number of prior periods; with either all + * or no blocks in the period signalling + * + * We establish the mask first, then consume "bools" until + * we run out of fuzz data to work out how many prior periods + * there are and which ones will signal. + */ + + // establish the mask + const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + + // mine prior periods + while (fuzzed_data_provider.remaining_bytes() > 0) { + // all blocks in these periods either do or don't signal + bool signal = fuzzed_data_provider.ConsumeBool(); + for (int b = 0; b < period; ++b) { + blocks.mine_block(signal); + } + + // don't risk exceeding max_blocks or times may wrap around + if (blocks.size() + period*2 > max_blocks) break; + } + // NOTE: fuzzed_data_provider may be fully consumed at this point and should not be used further + + // now we mine the final period and check that everything looks sane + + // count the number of signalling blocks + int blocks_sig = 0; + + // get the info for the first block of the period + CBlockIndex* prev = blocks.tip(); + const int exp_since = checker.GetStateSinceHeightFor(prev); + const ThresholdState exp_state = checker.GetStateFor(prev); + BIP9Stats last_stats = checker.GetStateStatisticsFor(prev); + + int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1); + assert(exp_since <= prev_next_height); + + // mine (period-1) blocks and check state + for (int b = 1; b < period; ++b) { + const bool signal = (signalling_mask >> (b % 32)) & 1; + if (signal) ++blocks_sig; + + CBlockIndex* current_block = blocks.mine_block(signal); + + // verify that signalling attempt was interpreted correctly + assert(checker.Condition(current_block) == signal); + + // state and since don't change within the period + const ThresholdState state = checker.GetStateFor(current_block); + const int since = checker.GetStateSinceHeightFor(current_block); + assert(state == exp_state); + assert(since == exp_since); + + // GetStateStatistics may crash when state is not STARTED + if (state != ThresholdState::STARTED) continue; + + // check that after mining this block stats change as expected + const BIP9Stats stats = checker.GetStateStatisticsFor(current_block); + assert(stats.period == period); + assert(stats.threshold == threshold); + assert(stats.elapsed == b); + assert(stats.count == last_stats.count + (signal ? 1 : 0)); + assert(stats.possible == (stats.count + period >= stats.elapsed + threshold)); + last_stats = stats; + } + + if (exp_state == ThresholdState::STARTED) { + // double check that stats.possible is sane + if (blocks_sig >= threshold - 1) assert(last_stats.possible); + } + + // mine the final block + bool signal = (signalling_mask >> (period % 32)) & 1; + if (signal) ++blocks_sig; + CBlockIndex* current_block = blocks.mine_block(signal); + assert(checker.Condition(current_block) == signal); + + // GetStateStatistics is safe on a period boundary + // and has progressed to a new period + const BIP9Stats stats = checker.GetStateStatisticsFor(current_block); + assert(stats.period == period); + assert(stats.threshold == threshold); + assert(stats.elapsed == 0); + assert(stats.count == 0); + assert(stats.possible == true); + + // More interesting is whether the state changed. + const ThresholdState state = checker.GetStateFor(current_block); + const int since = checker.GetStateSinceHeightFor(current_block); + + // since is straightforward: + assert(since % period == 0); + assert(0 <= since && since <= current_block->nHeight + 1); + if (state == exp_state) { + assert(since == exp_since); + } else { + assert(since == current_block->nHeight + 1); + } + + // state is where everything interesting is + switch (state) { + case ThresholdState::DEFINED: + assert(since == 0); + assert(exp_state == ThresholdState::DEFINED); + assert(current_block->GetMedianTimePast() < checker.m_begin); + assert(current_block->GetMedianTimePast() < checker.m_end); + break; + case ThresholdState::STARTED: + assert(current_block->GetMedianTimePast() >= checker.m_begin); + assert(current_block->GetMedianTimePast() < checker.m_end); + if (exp_state == ThresholdState::STARTED) { + assert(blocks_sig < threshold); + } else { + assert(exp_state == ThresholdState::DEFINED); + } + break; + case ThresholdState::LOCKED_IN: + assert(exp_state == ThresholdState::STARTED); + assert(current_block->GetMedianTimePast() < checker.m_end); + assert(blocks_sig >= threshold); + break; + case ThresholdState::ACTIVE: + assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN); + break; + case ThresholdState::FAILED: + assert(current_block->GetMedianTimePast() >= checker.m_end); + assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE); + break; + default: + assert(false); + } + + if (blocks.size() >= max_periods * period) { + // we chose the timeout (and block times) so that by the time we have this many blocks it's all over + assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED); + } + + // "always active" has additional restrictions + if (always_active_test) { + assert(state == ThresholdState::ACTIVE); + assert(exp_state == ThresholdState::ACTIVE); + assert(since == 0); + } else { + // except for always active, the initial state is always DEFINED + assert(since > 0 || state == ThresholdState::DEFINED); + assert(exp_since > 0 || exp_state == ThresholdState::DEFINED); + } + + // "never active" does too + if (never_active_test) { + assert(state == ThresholdState::FAILED); + assert(since == period); + if (exp_since == 0) { + assert(exp_state == ThresholdState::DEFINED); + } else { + assert(exp_state == ThresholdState::FAILED); + } + } +} diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 167242a971..c336705d50 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -8,8 +8,8 @@ #include <clientversion.h> #include <cstdint> #include <net.h> +#include <netaddress.h> #include <netbase.h> -#include <optional.h> #include <serialize.h> #include <span.h> #include <streams.h> @@ -24,6 +24,7 @@ #include <algorithm> #include <ios> #include <memory> +#include <optional> #include <string> using namespace std::literals; @@ -91,7 +92,7 @@ BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(cnode_listen_port) { // test default - uint16_t port = GetListenPort(); + uint16_t port{GetListenPort()}; BOOST_CHECK(port == Params().GetDefaultPort()); // test set port uint16_t altPort = 12345; @@ -827,7 +828,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::vector<NodeId>& node_ids, FastRandomContext& random_context) { Shuffle(candidates.begin(), candidates.end(), random_context); - const Optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates)); + const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates)); if (!evicted_node_id) { return false; } diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 66ad7bb5ea..33b56624a8 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <net_permissions.h> +#include <netaddress.h> #include <netbase.h> #include <protocol.h> #include <serialize.h> @@ -83,31 +84,31 @@ BOOST_AUTO_TEST_CASE(netbase_properties) } -bool static TestSplitHost(std::string test, std::string host, int port) +bool static TestSplitHost(const std::string& test, const std::string& host, uint16_t port) { std::string hostOut; - int portOut = -1; + uint16_t portOut{0}; SplitHostPort(test, portOut, hostOut); return hostOut == host && port == portOut; } BOOST_AUTO_TEST_CASE(netbase_splithost) { - BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", -1)); - BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", -1)); + BOOST_CHECK(TestSplitHost("www.bitcoincore.org", "www.bitcoincore.org", 0)); + BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]", "www.bitcoincore.org", 0)); BOOST_CHECK(TestSplitHost("www.bitcoincore.org:80", "www.bitcoincore.org", 80)); BOOST_CHECK(TestSplitHost("[www.bitcoincore.org]:80", "www.bitcoincore.org", 80)); - BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1)); + BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", 0)); BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333)); - BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1)); + BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", 0)); BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333)); - BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1)); + BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", 0)); BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333)); BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333)); - BOOST_CHECK(TestSplitHost("::8333", "::8333", -1)); + BOOST_CHECK(TestSplitHost("::8333", "::8333", 0)); BOOST_CHECK(TestSplitHost(":8333", "", 8333)); BOOST_CHECK(TestSplitHost("[]:8333", "", 8333)); - BOOST_CHECK(TestSplitHost("", "", -1)); + BOOST_CHECK(TestSplitHost("", "", 0)); } bool static TestParse(std::string src, std::string canon) diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp index 3e4b963fe3..496292875d 100644 --- a/src/test/sanity_tests.cpp +++ b/src/test/sanity_tests.cpp @@ -13,7 +13,6 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(basic_sanity) { - BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test"); BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test"); BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test"); BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test"); diff --git a/src/test/sock_tests.cpp b/src/test/sock_tests.cpp index ed9780dfb5..400de875b7 100644 --- a/src/test/sock_tests.cpp +++ b/src/test/sock_tests.cpp @@ -4,11 +4,13 @@ #include <compat.h> #include <test/util/setup_common.h> +#include <threadinterrupt.h> #include <util/sock.h> #include <util/system.h> #include <boost/test/unit_test.hpp> +#include <cassert> #include <thread> using namespace std::chrono_literals; @@ -144,6 +146,35 @@ BOOST_AUTO_TEST_CASE(wait) waiter.join(); } +BOOST_AUTO_TEST_CASE(recv_until_terminator_limit) +{ + constexpr auto timeout = 1min; // High enough so that it is never hit. + CThreadInterrupt interrupt; + int s[2]; + CreateSocketPair(s); + + Sock sock_send(s[0]); + Sock sock_recv(s[1]); + + std::thread receiver([&sock_recv, &timeout, &interrupt]() { + constexpr size_t max_data{10}; + bool threw_as_expected{false}; + // BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which + // creates a data race. So mimic it manually. + try { + sock_recv.RecvUntilTerminator('\n', timeout, interrupt, max_data); + } catch (const std::runtime_error& e) { + threw_as_expected = HasReason("too many bytes without a terminator")(e); + } + assert(threw_as_expected); + }); + + BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("1234567", timeout, interrupt)); + BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("89a\n", timeout, interrupt)); + + receiver.join(); +} + #endif /* WIN32 */ BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index b742fcaf0b..5ac09b05db 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -7,7 +7,6 @@ #include <clientversion.h> #include <hash.h> // For Hash() #include <key.h> // For CKey -#include <optional.h> #include <sync.h> #include <test/util/logging.h> #include <test/util/setup_common.h> @@ -23,6 +22,7 @@ #include <util/vector.h> #include <array> +#include <optional> #include <stdint.h> #include <string.h> #include <thread> @@ -38,6 +38,7 @@ #include <boost/test/unit_test.hpp> using namespace std::literals; +static const std::string STRING_WITH_EMBEDDED_NULL_CHAR{"1"s "\0" "1"s}; /* defined in logging.cpp */ namespace BCLog { @@ -76,6 +77,9 @@ BOOST_AUTO_TEST_CASE(util_check) const int two = *Assert(p_two); Assert(two == 2); Assert(true); + // Check that Assume can be used as unary expression + const bool result{Assume(two == 2)}; + Assert(result); } BOOST_AUTO_TEST_CASE(util_criticalsection) @@ -227,9 +231,9 @@ public: bool default_int = false; bool default_bool = false; const char* string_value = nullptr; - Optional<int64_t> int_value; - Optional<bool> bool_value; - Optional<std::vector<std::string>> list_value; + std::optional<int64_t> int_value; + std::optional<bool> bool_value; + std::optional<std::vector<std::string>> list_value; const char* error = nullptr; explicit Expect(util::SettingsValue s) : setting(std::move(s)) {} @@ -1272,7 +1276,7 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney) // Parsing strings with embedded NUL characters should fail BOOST_CHECK(!ParseMoney("\0-1"s, ret)); - BOOST_CHECK(!ParseMoney("\0" "1"s, ret)); + BOOST_CHECK(!ParseMoney(STRING_WITH_EMBEDDED_NULL_CHAR, ret)); BOOST_CHECK(!ParseMoney("1\0"s, ret)); } @@ -1449,10 +1453,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) BOOST_CHECK(!ParseInt32("1a", &n)); BOOST_CHECK(!ParseInt32("aap", &n)); BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex - BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseInt32(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseInt32("-2147483649", nullptr)); BOOST_CHECK(!ParseInt32("2147483648", nullptr)); @@ -1480,9 +1481,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64) BOOST_CHECK(!ParseInt64("1a", &n)); BOOST_CHECK(!ParseInt64("aap", &n)); BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseInt64(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseInt64("-9223372036854775809", nullptr)); BOOST_CHECK(!ParseInt64("9223372036854775808", nullptr)); @@ -1490,6 +1489,76 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64) BOOST_CHECK(!ParseInt64("32482348723847471234", nullptr)); } +BOOST_AUTO_TEST_CASE(test_ParseUInt8) +{ + uint8_t n; + // Valid values + BOOST_CHECK(ParseUInt8("255", nullptr)); + BOOST_CHECK(ParseUInt8("0", &n) && n == 0); + BOOST_CHECK(ParseUInt8("255", &n) && n == 255); + BOOST_CHECK(ParseUInt8("0255", &n) && n == 255); // no octal + BOOST_CHECK(ParseUInt8("255", &n) && n == static_cast<uint8_t>(255)); + BOOST_CHECK(ParseUInt8("+255", &n) && n == 255); + BOOST_CHECK(ParseUInt8("00000000000000000012", &n) && n == 12); + BOOST_CHECK(ParseUInt8("00000000000000000000", &n) && n == 0); + // Invalid values + BOOST_CHECK(!ParseUInt8("-00000000000000000000", &n)); + BOOST_CHECK(!ParseUInt8("", &n)); + BOOST_CHECK(!ParseUInt8(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseUInt8(" -1", &n)); + BOOST_CHECK(!ParseUInt8("++1", &n)); + BOOST_CHECK(!ParseUInt8("+-1", &n)); + BOOST_CHECK(!ParseUInt8("-+1", &n)); + BOOST_CHECK(!ParseUInt8("--1", &n)); + BOOST_CHECK(!ParseUInt8("-1", &n)); + BOOST_CHECK(!ParseUInt8("1 ", &n)); + BOOST_CHECK(!ParseUInt8("1a", &n)); + BOOST_CHECK(!ParseUInt8("aap", &n)); + BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex + BOOST_CHECK(!ParseUInt8(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); + // Overflow and underflow + BOOST_CHECK(!ParseUInt8("-255", &n)); + BOOST_CHECK(!ParseUInt8("256", &n)); + BOOST_CHECK(!ParseUInt8("-123", &n)); + BOOST_CHECK(!ParseUInt8("-123", nullptr)); + BOOST_CHECK(!ParseUInt8("256", nullptr)); +} + +BOOST_AUTO_TEST_CASE(test_ParseUInt16) +{ + uint16_t n; + // Valid values + BOOST_CHECK(ParseUInt16("1234", nullptr)); + BOOST_CHECK(ParseUInt16("0", &n) && n == 0); + BOOST_CHECK(ParseUInt16("1234", &n) && n == 1234); + BOOST_CHECK(ParseUInt16("01234", &n) && n == 1234); // no octal + BOOST_CHECK(ParseUInt16("65535", &n) && n == static_cast<uint16_t>(65535)); + BOOST_CHECK(ParseUInt16("+65535", &n) && n == 65535); + BOOST_CHECK(ParseUInt16("00000000000000000012", &n) && n == 12); + BOOST_CHECK(ParseUInt16("00000000000000000000", &n) && n == 0); + // Invalid values + BOOST_CHECK(!ParseUInt16("-00000000000000000000", &n)); + BOOST_CHECK(!ParseUInt16("", &n)); + BOOST_CHECK(!ParseUInt16(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseUInt16(" -1", &n)); + BOOST_CHECK(!ParseUInt16("++1", &n)); + BOOST_CHECK(!ParseUInt16("+-1", &n)); + BOOST_CHECK(!ParseUInt16("-+1", &n)); + BOOST_CHECK(!ParseUInt16("--1", &n)); + BOOST_CHECK(!ParseUInt16("-1", &n)); + BOOST_CHECK(!ParseUInt16("1 ", &n)); + BOOST_CHECK(!ParseUInt16("1a", &n)); + BOOST_CHECK(!ParseUInt16("aap", &n)); + BOOST_CHECK(!ParseUInt16("0x1", &n)); // no hex + BOOST_CHECK(!ParseUInt16(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); + // Overflow and underflow + BOOST_CHECK(!ParseUInt16("-65535", &n)); + BOOST_CHECK(!ParseUInt16("65536", &n)); + BOOST_CHECK(!ParseUInt16("-123", &n)); + BOOST_CHECK(!ParseUInt16("-123", nullptr)); + BOOST_CHECK(!ParseUInt16("65536", nullptr)); +} + BOOST_AUTO_TEST_CASE(test_ParseUInt32) { uint32_t n; @@ -1518,10 +1587,7 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt32) BOOST_CHECK(!ParseUInt32("1a", &n)); BOOST_CHECK(!ParseUInt32("aap", &n)); BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex - BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseUInt32(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseUInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseUInt32("-2147483648", &n)); BOOST_CHECK(!ParseUInt32("4294967296", &n)); @@ -1550,9 +1616,7 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt64) BOOST_CHECK(!ParseUInt64("1a", &n)); BOOST_CHECK(!ParseUInt64("aap", &n)); BOOST_CHECK(!ParseUInt64("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseUInt64(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseUInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseUInt64("-9223372036854775809", nullptr)); BOOST_CHECK(!ParseUInt64("18446744073709551616", nullptr)); @@ -1582,9 +1646,7 @@ BOOST_AUTO_TEST_CASE(test_ParseDouble) BOOST_CHECK(!ParseDouble("1a", &n)); BOOST_CHECK(!ParseDouble("aap", &n)); BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex - const char test_bytes[] = {'1', 0, '1'}; - std::string teststr(test_bytes, sizeof(test_bytes)); - BOOST_CHECK(!ParseDouble(teststr, &n)); // no embedded NULs + BOOST_CHECK(!ParseDouble(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); // Overflow and underflow BOOST_CHECK(!ParseDouble("-1e10000", nullptr)); BOOST_CHECK(!ParseDouble("1e10000", nullptr)); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f10b4ad740..67549fc13d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -8,7 +8,6 @@ #include <consensus/consensus.h> #include <consensus/tx_verify.h> #include <consensus/validation.h> -#include <optional.h> #include <policy/fees.h> #include <policy/policy.h> #include <policy/settings.h> @@ -19,6 +18,8 @@ #include <validation.h> #include <validationinterface.h> +#include <optional> + CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _entryHeight, bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp) @@ -159,7 +160,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr // GetMemPoolParents() is only valid for entries in the mempool, so we // iterate mapTx to find parents. for (unsigned int i = 0; i < tx.vin.size(); i++) { - Optional<txiter> piter = GetIter(tx.vin[i].prevout.hash); + std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash); if (piter) { staged_ancestors.insert(**piter); if (staged_ancestors.size() + 1 > limitAncestorCount) { @@ -890,11 +891,11 @@ const CTransaction* CTxMemPool::GetConflictTx(const COutPoint& prevout) const return it == mapNextTx.end() ? nullptr : it->second; } -Optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const +std::optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const { auto it = mapTx.find(txid); if (it != mapTx.end()) return it; - return Optional<txiter>{}; + return std::nullopt; } CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) const diff --git a/src/txmempool.h b/src/txmempool.h index 8a96294192..c3a9bd851d 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -8,6 +8,7 @@ #include <atomic> #include <map> +#include <optional> #include <set> #include <string> #include <utility> @@ -16,7 +17,6 @@ #include <amount.h> #include <coins.h> #include <indirectmap.h> -#include <optional.h> #include <policy/feerate.h> #include <primitives/transaction.h> #include <random.h> @@ -476,7 +476,7 @@ enum class MemPoolRemovalReason { */ class CTxMemPool { -private: +protected: const int m_check_ratio; //!< Value n means that 1 times in n we check. std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* minerPolicyEstimator; @@ -644,7 +644,7 @@ public: const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** Returns an iterator to the given hash, if found */ - Optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs); + std::optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** Translate a set of hashes into a set of pool iterators to avoid repeated lookups */ setEntries GetIterSet(const std::set<uint256>& hashes) const EXCLUSIVE_LOCKS_REQUIRED(cs); @@ -841,7 +841,7 @@ public: return m_epoch.visited(it->m_epoch_marker); } - bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch) + bool visited(std::optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch) { assert(m_epoch.guarded()); // verify guard even when it==nullopt return !it || visited(*it); diff --git a/src/util/check.h b/src/util/check.h index bc62da3440..e60088a2c6 100644 --- a/src/util/check.h +++ b/src/util/check.h @@ -69,7 +69,7 @@ T get_pure_r_value(T&& val) #ifdef ABORT_ON_FAILED_ASSUME #define Assume(val) Assert(val) #else -#define Assume(val) ((void)(val)) +#define Assume(val) ([&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); return std::forward<decltype(get_pure_r_value(val))>(check); }()) #endif #endif // BITCOIN_UTIL_CHECK_H diff --git a/src/util/sock.cpp b/src/util/sock.cpp index e13c52a16a..f9ecfef5d4 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -175,7 +175,8 @@ void Sock::SendComplete(const std::string& data, std::string Sock::RecvUntilTerminator(uint8_t terminator, std::chrono::milliseconds timeout, - CThreadInterrupt& interrupt) const + CThreadInterrupt& interrupt, + size_t max_data) const { const auto deadline = GetTime<std::chrono::milliseconds>() + timeout; std::string data; @@ -190,9 +191,14 @@ std::string Sock::RecvUntilTerminator(uint8_t terminator, // at a time is about 50 times slower. for (;;) { + if (data.size() >= max_data) { + throw std::runtime_error( + strprintf("Received too many bytes without a terminator (%u)", data.size())); + } + char buf[512]; - const ssize_t peek_ret{Recv(buf, sizeof(buf), MSG_PEEK)}; + const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)}; switch (peek_ret) { case -1: { diff --git a/src/util/sock.h b/src/util/sock.h index ecebb84205..4b0618dcff 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -135,13 +135,16 @@ public: * @param[in] terminator Character up to which to read from the socket. * @param[in] timeout Timeout for the entire operation. * @param[in] interrupt If this is signaled then the operation is canceled. + * @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes + * are received and there is still no terminator, then this method will throw an exception. * @return The data that has been read, without the terminating character. * @throws std::runtime_error if the operation cannot be completed. In this case some bytes may * have been consumed from the socket. */ virtual std::string RecvUntilTerminator(uint8_t terminator, std::chrono::milliseconds timeout, - CThreadInterrupt& interrupt) const; + CThreadInterrupt& interrupt, + size_t max_data) const; /** * Check if still connected. diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index f3d54a2ac9..4734de3e0b 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -107,23 +107,25 @@ std::vector<unsigned char> ParseHex(const std::string& str) return ParseHex(str.c_str()); } -void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { +void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut) +{ size_t colon = in.find_last_of(':'); // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator bool fHaveColon = colon != in.npos; - bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe - bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); - if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { - int32_t n; - if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { + bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe + bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos); + if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { + uint16_t n; + if (ParseUInt16(in.substr(colon + 1), &n)) { in = in.substr(0, colon); portOut = n; } } - if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') - hostOut = in.substr(1, in.size()-2); - else + if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') { + hostOut = in.substr(1, in.size() - 2); + } else { hostOut = in; + } } std::string EncodeBase64(Span<const unsigned char> input) @@ -334,6 +336,18 @@ bool ParseUInt8(const std::string& str, uint8_t *out) return true; } +bool ParseUInt16(const std::string& str, uint16_t* out) +{ + uint32_t u32; + if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint16_t>::max()) { + return false; + } + if (out != nullptr) { + *out = static_cast<uint16_t>(u32); + } + return true; +} + bool ParseUInt32(const std::string& str, uint32_t *out) { if (!ParsePrechecks(str)) diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 98379e9138..26dc0a0ce3 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -65,7 +65,7 @@ std::string EncodeBase32(Span<const unsigned char> input, bool pad = true); */ std::string EncodeBase32(const std::string& str, bool pad = true); -void SplitHostPort(std::string in, int& portOut, std::string& hostOut); +void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut); int64_t atoi64(const std::string& str); int atoi(const std::string& str); @@ -116,6 +116,13 @@ constexpr inline bool IsSpace(char c) noexcept { [[nodiscard]] bool ParseUInt8(const std::string& str, uint8_t *out); /** + * Convert decimal string to unsigned 16-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if the entire string could not be parsed or if overflow or underflow occurred. + */ +[[nodiscard]] bool ParseUInt16(const std::string& str, uint16_t* out); + +/** * Convert decimal string to unsigned 32-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, * false if not the entire string could be parsed or when overflow or underflow occurred. diff --git a/src/util/system.cpp b/src/util/system.cpp index 63b3c52ee5..0b83a76504 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -315,7 +315,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin if (key[0] != '-') { if (!m_accept_any_command && m_command.empty()) { // The first non-dash arg is a registered command - Optional<unsigned int> flags = GetArgFlags(key); + std::optional<unsigned int> flags = GetArgFlags(key); if (!flags || !(*flags & ArgsManager::COMMAND)) { error = strprintf("Invalid command '%s'", argv[i]); return false; @@ -337,7 +337,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin key.erase(0, 1); std::string section; util::SettingsValue value = InterpretOption(section, key, val); - Optional<unsigned int> flags = GetArgFlags('-' + key); + std::optional<unsigned int> flags = GetArgFlags('-' + key); // Unknown command line options and command line options with dot // characters (which are returned from InterpretOption with nonempty @@ -363,7 +363,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin return success; } -Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const +std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const { LOCK(cs_args); for (const auto& arg_map : m_available_args) { @@ -372,7 +372,7 @@ Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const return search->second.m_flags; } } - return nullopt; + return std::nullopt; } std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const @@ -874,7 +874,7 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file std::string section; std::string key = option.first; util::SettingsValue value = InterpretOption(section, key, option.second); - Optional<unsigned int> flags = GetArgFlags('-' + key); + std::optional<unsigned int> flags = GetArgFlags('-' + key); if (flags) { if (!CheckValid(key, value, *flags, error)) { return false; @@ -1034,7 +1034,7 @@ void ArgsManager::logArgsPrefix( std::string section_str = section.empty() ? "" : "[" + section + "] "; for (const auto& arg : args) { for (const auto& value : arg.second) { - Optional<unsigned int> flags = GetArgFlags('-' + arg.first); + std::optional<unsigned int> flags = GetArgFlags('-' + arg.first); if (flags) { std::string value_str = (*flags & SENSITIVE) ? "****" : value.write(); LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str); diff --git a/src/util/system.h b/src/util/system.h index 30b8cb1c68..291f3f5541 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -19,7 +19,6 @@ #include <compat/assumptions.h> #include <fs.h> #include <logging.h> -#include <optional.h> #include <sync.h> #include <tinyformat.h> #include <util/settings.h> @@ -28,6 +27,7 @@ #include <exception> #include <map> +#include <optional> #include <set> #include <stdint.h> #include <string> @@ -375,7 +375,7 @@ public: * Return Flags for known arg. * Return nullopt for unknown arg. */ - Optional<unsigned int> GetArgFlags(const std::string& name) const; + std::optional<unsigned int> GetArgFlags(const std::string& name) const; /** * Read and update settings file with saved settings. This needs to be diff --git a/src/util/tokenpipe.cpp b/src/util/tokenpipe.cpp index 79465dd430..4c091cd2e6 100644 --- a/src/util/tokenpipe.cpp +++ b/src/util/tokenpipe.cpp @@ -9,6 +9,7 @@ #include <errno.h> #include <fcntl.h> +#include <optional> #include <unistd.h> TokenPipeEnd TokenPipe::TakeReadEnd() diff --git a/src/validation.cpp b/src/validation.cpp index be0be904e9..d1b9efe7ba 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -23,7 +23,6 @@ #include <logging/timer.h> #include <node/coinstats.h> #include <node/ui_interface.h> -#include <optional.h> #include <policy/policy.h> #include <policy/settings.h> #include <pow.h> @@ -50,6 +49,7 @@ #include <validationinterface.h> #include <warnings.h> +#include <optional> #include <string> #include <boost/algorithm/string/replace.hpp> @@ -2974,6 +2974,10 @@ bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams& bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { + // Genesis block can't be invalidated + assert(pindex); + if (pindex->nHeight == 0) return false; + CBlockIndex* to_mark_failed = pindex; bool pindex_was_in_chain = false; int disconnected = 0; @@ -5010,11 +5014,11 @@ CBlockFileInfo* GetBlockFileInfo(size_t n) static const uint64_t MEMPOOL_DUMP_VERSION = 1; -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function) { const CChainParams& chainparams = Params(); int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")}; CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); @@ -5095,7 +5099,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) return true; } -bool DumpMempool(const CTxMemPool& pool) +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit) { int64_t start = GetTimeMicros(); @@ -5118,7 +5122,7 @@ bool DumpMempool(const CTxMemPool& pool) int64_t mid = GetTimeMicros(); try { - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")}; if (!filestr) { return false; } @@ -5141,7 +5145,7 @@ bool DumpMempool(const CTxMemPool& pool) LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size()); file << unbroadcast_txids; - if (!FileCommit(file.Get())) + if (!skip_file_commit && !FileCommit(file.Get())) throw std::runtime_error("FileCommit failed"); file.fclose(); if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) { @@ -5175,14 +5179,14 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin return std::min<double>(pindex->nChainTx / fTxTotal, 1.0); } -Optional<uint256> ChainstateManager::SnapshotBlockhash() const { +std::optional<uint256> ChainstateManager::SnapshotBlockhash() const { LOCK(::cs_main); if (m_active_chainstate != nullptr && !m_active_chainstate->m_from_snapshot_blockhash.IsNull()) { // If a snapshot chainstate exists, it will always be our active. return m_active_chainstate->m_from_snapshot_blockhash; } - return {}; + return std::nullopt; } std::vector<CChainState*> ChainstateManager::GetAll() diff --git a/src/validation.h b/src/validation.h index 512b306219..2ff5f4ac87 100644 --- a/src/validation.h +++ b/src/validation.h @@ -17,7 +17,6 @@ #include <crypto/common.h> // for ReadLE64 #include <fs.h> #include <node/utxo_snapshot.h> -#include <optional.h> #include <policy/feerate.h> #include <protocol.h> // For CMessageHeader::MessageStartChars #include <script/script_error.h> @@ -32,6 +31,7 @@ #include <atomic> #include <map> #include <memory> +#include <optional> #include <set> #include <stdint.h> #include <string> @@ -205,8 +205,7 @@ struct MempoolAcceptResult { /** Constructor for failure case */ explicit MempoolAcceptResult(TxValidationState state) - : m_result_type(ResultType::INVALID), - m_state(state), m_replaced_transactions(nullopt), m_base_fees(nullopt) { + : m_result_type(ResultType::INVALID), m_state(state) { Assume(!state.IsValid()); // Can be invalid or error } @@ -924,7 +923,7 @@ public: bool IsSnapshotActive() const; - Optional<uint256> SnapshotBlockhash() const; + std::optional<uint256> SnapshotBlockhash() const; //! Is there a snapshot in use and has it been fully validated? bool IsSnapshotValidated() const { return m_snapshot_validated; } @@ -1013,11 +1012,13 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para /** Get block file info entry for one block file */ CBlockFileInfo* GetBlockFileInfo(size_t n); +using FopenFn = std::function<FILE*(const fs::path&, const char*)>; + /** Dump the mempool to disk. */ -bool DumpMempool(const CTxMemPool& pool); +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false); /** Load the mempool from disk. */ -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate); +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen); //! Check whether the block associated with this index entry is pruned or not. inline bool IsBlockPruned(const CBlockIndex* pblockindex) diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index c499b0ff25..d25a3fb3fa 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -5,13 +5,14 @@ #ifndef BITCOIN_WALLET_COINCONTROL_H #define BITCOIN_WALLET_COINCONTROL_H -#include <optional.h> #include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> #include <primitives/transaction.h> #include <script/standard.h> +#include <optional> + const int DEFAULT_MIN_DEPTH = 0; const int DEFAULT_MAX_DEPTH = 9999999; @@ -25,7 +26,7 @@ public: //! Custom change destination, if not set an address is generated CTxDestination destChange; //! Override the default change type if set, ignored if destChange is set - Optional<OutputType> m_change_type; + std::optional<OutputType> m_change_type; //! If false, only selected inputs are used bool m_add_inputs; //! If false, allows unselected inputs, but requires all selected inputs be used @@ -35,11 +36,11 @@ public: //! Override automatic min/max checks on fee, m_feerate must be set if true bool fOverrideFeeRate; //! Override the wallet's m_pay_tx_fee if set - Optional<CFeeRate> m_feerate; + std::optional<CFeeRate> m_feerate; //! Override the default confirmation target if set - Optional<unsigned int> m_confirm_target; + std::optional<unsigned int> m_confirm_target; //! Override the wallet's m_signal_rbf if set - Optional<bool> m_signal_bip125_rbf; + std::optional<bool> m_signal_bip125_rbf; //! Avoid partial use of funds sent to a given address bool m_avoid_partial_spends; //! Forbids inclusion of dirty (previously used) addresses diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 10f89e3a6f..5a18308a73 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -4,11 +4,12 @@ #include <wallet/coinselection.h> -#include <optional.h> #include <policy/feerate.h> #include <util/system.h> #include <util/moneystr.h> +#include <optional> + // Descending order comparator struct { bool operator()(const OutputGroup& a, const OutputGroup& b) const @@ -222,7 +223,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group nValueRet = 0; // List of values less than target - Optional<OutputGroup> lowest_larger; + std::optional<OutputGroup> lowest_larger; std::vector<OutputGroup> applicable_groups; CAmount nTotalLower = 0; diff --git a/src/wallet/db.h b/src/wallet/db.h index b64e9fa533..7a0d3d2e07 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -8,12 +8,12 @@ #include <clientversion.h> #include <fs.h> -#include <optional.h> #include <streams.h> #include <support/allocators/secure.h> #include <atomic> #include <memory> +#include <optional> #include <string> struct bilingual_str; @@ -203,7 +203,7 @@ enum class DatabaseFormat { struct DatabaseOptions { bool require_existing = false; bool require_create = false; - Optional<DatabaseFormat> require_format; + std::optional<DatabaseFormat> require_format; uint64_t create_flags = 0; SecureString create_passphrase; bool verify = true; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index f3e24384df..fdeead1fa5 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -70,7 +70,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); #if HAVE_SYSTEM - argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID, %w is replaced by wallet name, %b is replaced by the hash of the block including the transaction (set to 'unconfirmed' if the transaction is not included) and %h is replaced by the block height (-1 if not included). %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); #endif argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 30832f983b..4543f6fb4c 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -154,7 +154,7 @@ void UnloadWallets() auto wallet = wallets.back(); wallets.pop_back(); std::vector<bilingual_str> warnings; - RemoveWallet(wallet, nullopt, warnings); + RemoveWallet(wallet, std::nullopt, warnings); UnloadWallet(std::move(wallet)); } } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 50423c3e32..0737e68cf3 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1788,7 +1788,7 @@ RPCHelpMan listdescriptors() const bool active = active_spk_mans.count(desc_spk_man) != 0; spk.pushKV("active", active); const auto& type = wallet_descriptor.descriptor->GetOutputType(); - if (active && type != nullopt) { + if (active && type) { spk.pushKV("internal", wallet->GetScriptPubKeyMan(*type, true) == desc_spk_man); } if (wallet_descriptor.descriptor->IsRange()) { diff --git a/src/wallet/rpcsigner.cpp b/src/wallet/rpcsigner.cpp index 607b778c68..696c74d665 100644 --- a/src/wallet/rpcsigner.cpp +++ b/src/wallet/rpcsigner.cpp @@ -32,9 +32,6 @@ static RPCHelpMan enumeratesigners() }, RPCExamples{""}, [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - if (!wallet) return NullUniValue; - const std::string command = gArgs.GetArg("-signer", ""); if (command == "") throw JSONRPCError(RPC_WALLET_ERROR, "Error: restart bitcoind with -signer=<cmd>"); std::string chain = gArgs.GetChainName(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1b9071a712..6dc8d1de42 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8,7 +8,6 @@ #include <interfaces/chain.h> #include <key_io.h> #include <node/context.h> -#include <optional.h> #include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> @@ -38,6 +37,7 @@ #include <wallet/walletdb.h> #include <wallet/walletutil.h> +#include <optional> #include <stdint.h> #include <univalue.h> @@ -219,7 +219,7 @@ static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const Un cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN); if (override_min_fee) cc.fOverrideFeeRate = true; // Default RBF to true for explicit fee_rate, if unset. - if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true; + if (!cc.m_signal_bip125_rbf) cc.m_signal_bip125_rbf = true; return; } if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) { @@ -1564,8 +1564,8 @@ static RPCHelpMan listsinceblock() LOCK(wallet.cs_wallet); - Optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain. - Optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain. + std::optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain. + std::optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain. int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; @@ -2590,7 +2590,7 @@ static RPCHelpMan loadwallet() options.require_existing = true; bilingual_str error; std::vector<bilingual_str> warnings; - Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool()); + std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool()); std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings); if (!wallet) { // Map bad format to not found, since bad format is returned when the @@ -2764,7 +2764,7 @@ static RPCHelpMan createwallet() options.create_flags = flags; options.create_passphrase = passphrase; bilingual_str error; - Optional<bool> load_on_start = request.params[6].isNull() ? nullopt : Optional<bool>(request.params[6].get_bool()); + std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool()); std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings); if (!wallet) { RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR; @@ -2816,7 +2816,7 @@ static RPCHelpMan unloadwallet() // Note that any attempt to load the same wallet would fail until the wallet // is destroyed (see CheckUniqueFileid). std::vector<bilingual_str> warnings; - Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool()); + std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool()); if (!RemoveWallet(wallet, load_on_start, warnings)) { throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded"); } @@ -3587,7 +3587,7 @@ static RPCHelpMan rescanblockchain() } int start_height = 0; - Optional<int> stop_height; + std::optional<int> stop_height; uint256 start_block; { LOCK(pwallet->cs_wallet); diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index a8200981d8..54319ca662 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -16,6 +16,8 @@ #include <wallet/external_signer.h> #include <wallet/scriptpubkeyman.h> +#include <optional> + //! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details. const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; @@ -1607,7 +1609,7 @@ bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDest { LOCK(cs_desc_man); assert(m_wallet_descriptor.descriptor->IsSingleType()); // This is a combo descriptor which should not be an active descriptor - Optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType(); + std::optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType(); assert(desc_addr_type); if (type != *desc_addr_type) { throw std::runtime_error(std::string(__func__) + ": Types are inconsistent"); @@ -1629,7 +1631,7 @@ bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDest return false; } - Optional<OutputType> out_script_type = m_wallet_descriptor.descriptor->GetOutputType(); + std::optional<OutputType> out_script_type = m_wallet_descriptor.descriptor->GetOutputType(); if (out_script_type && out_script_type == type) { ExtractDestination(scripts_temp[0], dest); } else { diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 0e9f3819eb..7eff6e592d 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -35,7 +35,10 @@ static CAmount balance = 0; CoinEligibilityFilter filter_standard(1, 6, 0); CoinEligibilityFilter filter_confirmed(1, 1, 0); CoinEligibilityFilter filter_standard_extra(6, 6, 0); -CoinSelectionParams coin_selection_params(false, 0, 0, CFeeRate(0), 0, false); +CoinSelectionParams coin_selection_params(/* use_bnb= */ false, /* change_output_size= */ 0, + /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0), + /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), + /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set) { @@ -269,7 +272,10 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) } // Make sure that effective value is working in SelectCoinsMinConf when BnB is used - CoinSelectionParams coin_selection_params_bnb(true, 0, 0, CFeeRate(3000), 0, false); + CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 0, + /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000), + /* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000), + /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); CoinSet setCoinsRet; CAmount nValueRet; bool bnb_used; @@ -301,7 +307,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) CCoinControl coin_control; coin_control.fAllowOtherInputs = true; coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i)); - coin_selection_params_bnb.effective_fee = CFeeRate(0); + coin_selection_params_bnb.m_effective_feerate = CFeeRate(0); BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used)); BOOST_CHECK(bnb_used); BOOST_CHECK(coin_selection_params_bnb.use_bnb); @@ -639,8 +645,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) CAmount target = rand.randrange(balance - 1000) + 1000; // Perform selection - CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0, false); - CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0, false); + CoinSelectionParams coin_selection_params_knapsack(/* use_bnb= */ false, /* change_output_size= */ 34, + /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), + /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), + /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); + CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 34, + /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), + /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), + /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); CoinSet out_set; CAmount out_value = 0; bool bnb_used = false; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 4598999b3c..30cc452065 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -228,7 +228,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup) "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); - RemoveWallet(wallet, nullopt); + RemoveWallet(wallet, std::nullopt); } } @@ -271,7 +271,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) request.params.push_back(backup_file); ::dumpwallet().HandleRequest(request); - RemoveWallet(wallet, nullopt); + RemoveWallet(wallet, std::nullopt); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME @@ -288,7 +288,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) AddWallet(wallet); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); ::importwallet().HandleRequest(request); - RemoveWallet(wallet, nullopt); + RemoveWallet(wallet, std::nullopt); BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U); BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 58ab124061..b00fa851fd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -13,7 +13,6 @@ #include <interfaces/wallet.h> #include <key.h> #include <key_io.h> -#include <optional.h> #include <outputtype.h> #include <policy/fees.h> #include <policy/policy.h> @@ -40,6 +39,7 @@ #include <algorithm> #include <assert.h> +#include <optional> #include <boost/algorithm/string/replace.hpp> @@ -84,10 +84,10 @@ bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_nam static void UpdateWalletSetting(interfaces::Chain& chain, const std::string& wallet_name, - Optional<bool> load_on_startup, + std::optional<bool> load_on_startup, std::vector<bilingual_str>& warnings) { - if (load_on_startup == nullopt) return; + if (!load_on_startup) return; if (load_on_startup.value() && !AddWalletSetting(chain, wallet_name)) { warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup.")); } else if (!load_on_startup.value() && !RemoveWalletSetting(chain, wallet_name)) { @@ -107,7 +107,7 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet) return true; } -bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings) +bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings) { assert(wallet); @@ -127,7 +127,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on return true; } -bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start) +bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start) { std::vector<bilingual_str> warnings; return RemoveWallet(wallet, load_on_start, warnings); @@ -204,7 +204,7 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet) } namespace { -std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) +std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) { try { std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error); @@ -234,11 +234,11 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std: } } // namespace -std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) { auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name)); if (!result.second) { - error = Untranslated("Wallet already being loading."); + error = Untranslated("Wallet already loading."); status = DatabaseStatus::FAILED_LOAD; return nullptr; } @@ -247,7 +247,7 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& return wallet; } -std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) +std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) { uint64_t wallet_creation_flags = options.create_flags; const SecureString& passphrase = options.create_passphrase; @@ -944,6 +944,14 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", hash.GetHex()); + if (confirm.status == CWalletTx::Status::CONFIRMED) + { + boost::replace_all(strCmd, "%b", confirm.hashBlock.GetHex()); + boost::replace_all(strCmd, "%h", ToString(confirm.block_height)); + } else { + boost::replace_all(strCmd, "%b", "unconfirmed"); + boost::replace_all(strCmd, "%h", "-1"); + } #ifndef WIN32 // Substituting the wallet name isn't currently supported on windows // because windows shell escaping has not been implemented yet: @@ -1763,7 +1771,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r * the main chain after to the addition of any new keys you want to detect * transactions for. */ -CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate) +CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate) { int64_t nNow = GetTime(); int64_t start_time = GetTimeMillis(); @@ -2391,26 +2399,20 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil nValueRet = 0; if (coin_selection_params.use_bnb) { - // Get long term estimate - FeeCalculation feeCalc; - CCoinControl temp; - temp.m_confirm_target = 1008; - CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc); - // Get the feerate for effective value. // When subtracting the fee from the outputs, we want the effective feerate to be 0 CFeeRate effective_feerate{0}; if (!coin_selection_params.m_subtract_fee_outputs) { - effective_feerate = coin_selection_params.effective_fee; + effective_feerate = coin_selection_params.m_effective_feerate; } - std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, long_term_feerate, eligibility_filter, true /* positive_only */); + std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, true /* positive_only */); // Calculate cost of change - CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size); + CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size); // Calculate the fees for things that aren't inputs - CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size); + CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size); bnb_used = true; return SelectCoinsBnB(groups, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees); } else { @@ -2464,7 +2466,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm if (coin.m_input_bytes <= 0) { return false; // Not solvable, can't estimate size for fee } - coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes); + coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes); if (coin_selection_params.use_bnb) { value_to_select -= coin.effective_value; } else { @@ -2725,7 +2727,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin return locktime; } -OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const +OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const { // If -changetype is specified, always use that change type. if (change_type) { @@ -2832,17 +2834,28 @@ bool CWallet::CreateTransactionInternal( CTxOut change_prototype_txout(0, scriptChange); coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); - CFeeRate discard_rate = GetDiscardRate(*this); + // Set discard feerate + coin_selection_params.m_discard_feerate = GetDiscardRate(*this); // Get the fee rate to use effective values in coin selection - CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc); + coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc); // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly // provided one - if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) { - error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), nFeeRateNeeded.ToString(FeeEstimateMode::SAT_VB)); + if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) { + error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB)); + return false; + } + if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { + // eventually allow a fallback fee + error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); return false; } + // Get long term estimate + CCoinControl cc_temp; + cc_temp.m_confirm_target = chain().estimateMaxBlocks(); + coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr); + nFeeRet = 0; bool pick_new_inputs = true; CAmount nValueIn = 0; @@ -2916,7 +2929,6 @@ bool CWallet::CreateTransactionInternal( } else { coin_selection_params.change_spend_size = (size_t)change_spend_size; } - coin_selection_params.effective_fee = nFeeRateNeeded; if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used)) { // If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack. @@ -2942,7 +2954,7 @@ bool CWallet::CreateTransactionInternal( // Never create dust outputs; if we would, just // add the dust to the fee. // The nChange when BnB is used is always going to go to fees. - if (IsDust(newTxOut, discard_rate) || bnb_used) + if (IsDust(newTxOut, coin_selection_params.m_discard_feerate) || bnb_used) { nChangePosInOut = -1; nFeeRet += nChange; @@ -2980,13 +2992,7 @@ bool CWallet::CreateTransactionInternal( return false; } - nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc); - if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { - // eventually allow a fallback fee - error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); - return false; - } - + nFeeNeeded = coin_selection_params.m_effective_feerate.GetFee(nBytes); if (nFeeRet >= nFeeNeeded) { // Reduce fee to only the needed amount if possible. This // prevents potential overpayment in fees if the coins @@ -3000,8 +3006,8 @@ bool CWallet::CreateTransactionInternal( // change output. Only try this once. if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size - CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr); - CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); + CAmount fee_needed_with_change = coin_selection_params.m_effective_feerate.GetFee(tx_size_with_change); + CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate); if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) { pick_new_inputs = false; nFeeRet = fee_needed_with_change; @@ -4067,13 +4073,13 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st WalletBatch batch(walletInstance->GetDatabase()); CBlockLocator locator; if (batch.ReadBestBlock(locator)) { - if (const Optional<int> fork_height = chain.findLocatorFork(locator)) { + if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) { rescan_height = *fork_height; } } } - const Optional<int> tip_height = chain.getHeight(); + const std::optional<int> tip_height = chain.getHeight(); if (tip_height) { walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height); walletInstance->m_last_block_processed_height = *tip_height; @@ -4107,7 +4113,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st // No need to read and scan block if block was created before // our wallet birthday (as adjusted for block time variability) - Optional<int64_t> time_first_key; + std::optional<int64_t> time_first_key; for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) { int64_t time = spk_man->GetTimeFirstKey(); if (!time_first_key || time < *time_first_key) time_first_key = time; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3a76163dd2..6031d36b97 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -30,6 +30,7 @@ #include <atomic> #include <map> #include <memory> +#include <optional> #include <set> #include <stdexcept> #include <stdint.h> @@ -51,12 +52,12 @@ struct bilingual_str; void UnloadWallet(std::shared_ptr<CWallet>&& wallet); bool AddWallet(const std::shared_ptr<CWallet>& wallet); -bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings); -bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start); +bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings); +bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start); std::vector<std::shared_ptr<CWallet>> GetWallets(); std::shared_ptr<CWallet> GetWallet(const std::string& name); -std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); -std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); +std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); +std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet); std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); @@ -608,17 +609,22 @@ struct CoinSelectionParams bool use_bnb = true; size_t change_output_size = 0; size_t change_spend_size = 0; - CFeeRate effective_fee = CFeeRate(0); + CFeeRate m_effective_feerate; + CFeeRate m_long_term_feerate; + CFeeRate m_discard_feerate; size_t tx_noinputs_size = 0; //! Indicate that we are subtracting the fee from outputs bool m_subtract_fee_outputs = false; bool m_avoid_partial_spends = false; - CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size, bool avoid_partial) : + CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate, + CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size, bool avoid_partial) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), - effective_fee(effective_fee), + m_effective_feerate(effective_feerate), + m_long_term_feerate(long_term_feerate), + m_discard_feerate(discard_feerate), tx_noinputs_size(tx_noinputs_size), m_avoid_partial_spends(avoid_partial) {} @@ -923,7 +929,7 @@ public: //! Unset if no blocks were scanned due to read errors or the chain //! being empty. uint256 last_scanned_block; - Optional<int> last_scanned_height; + std::optional<int> last_scanned_height; //! Height of the most recent block that could not be scanned due to //! read errors or pruning. Will be set if status is FAILURE, unset if @@ -931,7 +937,7 @@ public: //! USER_ABORT. uint256 last_failed_block; }; - ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate); + ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate); void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override; void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ResendWalletTransactions(); @@ -946,7 +952,7 @@ public: Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const; CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const; - OutputType TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const; + OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const; /** * Insert additional inputs into the transaction by @@ -1031,7 +1037,7 @@ public: * (see -changetype option documentation and implementation in * CWallet::TransactionChangeType for details). */ - Optional<OutputType> m_default_change_type{}; + std::optional<OutputType> m_default_change_type{}; /** Absolute maximum transaction fee (in satoshis) used by default for the wallet */ CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE}; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index a5a454f9a8..3d9248009f 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -23,6 +23,7 @@ #include <wallet/wallet.h> #include <atomic> +#include <optional> #include <string> namespace DBKeys { @@ -1015,7 +1016,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas return nullptr; } - Optional<DatabaseFormat> format; + std::optional<DatabaseFormat> format; if (exists) { if (IsBDBFile(BDBDataFile(path))) { format = DatabaseFormat::BERKELEY; |