diff options
Diffstat (limited to 'src')
44 files changed, 1037 insertions, 401 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include index b4ca01b1fe..6edbe77776 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -4,11 +4,15 @@ FUZZ_TARGETS = \ + test/fuzz/addr_info_deserialize \ test/fuzz/address_deserialize \ test/fuzz/addrman_deserialize \ test/fuzz/banentry_deserialize \ test/fuzz/bech32 \ test/fuzz/block_deserialize \ + test/fuzz/block_file_info_deserialize \ + test/fuzz/block_filter_deserialize \ + test/fuzz/block_header_and_short_txids_deserialize \ test/fuzz/blockheader_deserialize \ test/fuzz/blocklocator_deserialize \ test/fuzz/blockmerkleroot \ @@ -20,16 +24,33 @@ FUZZ_TARGETS = \ test/fuzz/descriptor_parse \ test/fuzz/diskblockindex_deserialize \ test/fuzz/eval_script \ + test/fuzz/fee_rate_deserialize \ + test/fuzz/flat_file_pos_deserialize \ + test/fuzz/integer \ test/fuzz/inv_deserialize \ + test/fuzz/key_origin_info_deserialize \ + test/fuzz/merkle_block_deserialize \ test/fuzz/messageheader_deserialize \ test/fuzz/netaddr_deserialize \ + test/fuzz/out_point_deserialize \ test/fuzz/parse_iso8601 \ + test/fuzz/partial_merkle_tree_deserialize \ + test/fuzz/partially_signed_transaction_deserialize \ + test/fuzz/prefilled_transaction_deserialize \ test/fuzz/psbt \ + test/fuzz/psbt_input_deserialize \ + test/fuzz/psbt_output_deserialize \ + test/fuzz/pub_key_deserialize \ test/fuzz/script \ + test/fuzz/script_deserialize \ test/fuzz/script_flags \ test/fuzz/service_deserialize \ test/fuzz/spanparsing \ + test/fuzz/sub_net_deserialize \ test/fuzz/transaction \ + test/fuzz/tx_in \ + test/fuzz/tx_in_deserialize \ + test/fuzz/tx_out \ test/fuzz/txoutcompressor_deserialize \ test/fuzz/txundo_deserialize @@ -347,6 +368,12 @@ test_fuzz_eval_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_eval_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_eval_script_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_integer_SOURCES = $(FUZZ_SUITE) test/fuzz/integer.cpp +test_fuzz_integer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_integer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_integer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_integer_LDADD = $(FUZZ_SUITE_LD_COMMON) + test_fuzz_txoutcompressor_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp test_fuzz_txoutcompressor_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXOUTCOMPRESSOR_DESERIALIZE=1 test_fuzz_txoutcompressor_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -371,6 +398,126 @@ test_fuzz_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_transaction_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_addr_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_addr_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDR_INFO_DESERIALIZE=1 +test_fuzz_addr_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_addr_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_addr_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_block_file_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_block_file_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILE_INFO_DESERIALIZE=1 +test_fuzz_block_file_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_file_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_file_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_block_filter_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_block_filter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILTER_DESERIALIZE=1 +test_fuzz_block_filter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_filter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_filter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_block_header_and_short_txids_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_block_header_and_short_txids_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE=1 +test_fuzz_block_header_and_short_txids_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_header_and_short_txids_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_fee_rate_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_fee_rate_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFEE_RATE_DESERIALIZE=1 +test_fuzz_fee_rate_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_fee_rate_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_fee_rate_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_flat_file_pos_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_flat_file_pos_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFLAT_FILE_POS_DESERIALIZE=1 +test_fuzz_flat_file_pos_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_key_origin_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1 +test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_merkle_block_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_merkle_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMERKLE_BLOCK_DESERIALIZE=1 +test_fuzz_merkle_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_merkle_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_merkle_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_out_point_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_out_point_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DOUT_POINT_DESERIALIZE=1 +test_fuzz_out_point_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_out_point_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_out_point_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_partially_signed_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_partially_signed_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIALLY_SIGNED_TRANSACTION_DESERIALIZE=1 +test_fuzz_partially_signed_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_partially_signed_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_partial_merkle_tree_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_partial_merkle_tree_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIAL_MERKLE_TREE_DESERIALIZE=1 +test_fuzz_partial_merkle_tree_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_partial_merkle_tree_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_partial_merkle_tree_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_prefilled_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_prefilled_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPREFILLED_TRANSACTION_DESERIALIZE=1 +test_fuzz_prefilled_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_prefilled_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_psbt_input_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_psbt_input_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_INPUT_DESERIALIZE=1 +test_fuzz_psbt_input_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_psbt_input_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_psbt_input_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_psbt_output_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_psbt_output_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_OUTPUT_DESERIALIZE=1 +test_fuzz_psbt_output_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_psbt_output_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_psbt_output_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_pub_key_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_pub_key_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPUB_KEY_DESERIALIZE=1 +test_fuzz_pub_key_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_pub_key_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_pub_key_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_script_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_script_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSCRIPT_DESERIALIZE=1 +test_fuzz_script_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_script_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_script_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_sub_net_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_sub_net_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSUB_NET_DESERIALIZE=1 +test_fuzz_sub_net_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_sub_net_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_sub_net_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_tx_in_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_tx_in_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTX_IN_DESERIALIZE=1 +test_fuzz_tx_in_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_tx_in_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_tx_in_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_tx_in_SOURCES = $(FUZZ_SUITE) test/fuzz/tx_in.cpp +test_fuzz_tx_in_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_tx_in_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_tx_in_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_tx_in_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_tx_out_SOURCES = $(FUZZ_SUITE) test/fuzz/tx_out.cpp +test_fuzz_tx_out_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_tx_out_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_tx_out_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_tx_out_LDADD = $(FUZZ_SUITE_LD_COMMON) + endif # ENABLE_FUZZ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 592fcbe8dd..1035e730b8 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -256,7 +256,6 @@ public: return batch[ID_BLOCKCHAININFO]; } result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); - result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]); result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]); result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]); @@ -266,9 +265,7 @@ public: result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"])); if (!batch[ID_WALLETINFO]["result"].isNull()) { - result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); - result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]); result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index 917ecd71c5..a2013edc05 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -41,8 +41,8 @@ static bool WalletAppInit(int argc, char* argv[]) } if (argc < 2 || HelpRequested(gArgs)) { std::string usage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" + - "wallet-tool is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" + - "By default wallet-tool will act on wallets in the default mainnet wallet directory in the datadir.\n" + + "bitcoin-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" + + "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" + "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" + "Usage:\n" + " bitcoin-wallet [options] <command>\n\n" + diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 674f47b956..4ad5818cdc 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -6,11 +6,11 @@ #define BITCOIN_CUCKOOCACHE_H #include <array> -#include <algorithm> #include <atomic> -#include <cstring> #include <cmath> +#include <cstring> #include <memory> +#include <utility> #include <vector> diff --git a/src/net.cpp b/src/net.cpp index 84692d2a79..99dae88bab 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,6 +16,7 @@ #include <crypto/sha256.h> #include <netbase.h> #include <net_permissions.h> +#include <random.h> #include <scheduler.h> #include <ui_interface.h> #include <util/strencodings.h> @@ -445,6 +446,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only); pnode->AddRef(); + // We're making a new connection, harvest entropy from the time (and our peer count) + RandAddEvent((uint32_t)id); + return pnode; } @@ -693,6 +697,9 @@ CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageSta msg.m_message_size = hdr.nMessageSize; msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE; + // We just received a message off the wire, harvest entropy from the time (and the message checksum) + RandAddEvent(ReadLE32(hash.begin())); + msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0); if (!msg.m_valid_checksum) { LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n", @@ -1017,6 +1024,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { LOCK(cs_vNodes); vNodes.push_back(pnode); } + + // We received a new connection, harvest entropy from the time (and our peer count) + RandAddEvent((uint32_t)id); } void CConnman::DisconnectNodes() diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index 12559c5a5f..9a30c3f083 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -7,6 +7,7 @@ #include <node/psbt.h> #include <policy/policy.h> #include <policy/settings.h> +#include <tinyformat.h> #include <numeric> @@ -39,6 +40,11 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) calc_fee = false; } + if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) { + result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i)); + return result; + } + // Check if it is final if (!utxo.IsNull() && !PSBTInputSigned(input)) { input_analysis.is_final = false; diff --git a/src/node/psbt.h b/src/node/psbt.h index e04366a20f..7384dc415c 100644 --- a/src/node/psbt.h +++ b/src/node/psbt.h @@ -30,6 +30,17 @@ struct PSBTAnalysis { 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; + inputs.clear(); + next = PSBTRole::CREATOR; + error = err_msg; + } }; /** diff --git a/src/prevector.h b/src/prevector.h index d307495fbe..4fb07688ff 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -13,6 +13,7 @@ #include <algorithm> #include <cstddef> #include <type_traits> +#include <utility> #pragma pack(push, 1) /** Implements a drop-in replacement for std::vector<T> which stores up to N diff --git a/src/psbt.cpp b/src/psbt.cpp index c306079b1e..9ede62efdf 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -348,6 +348,7 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector std::string PSBTRoleName(PSBTRole role) { switch (role) { + case PSBTRole::CREATOR: return "creator"; case PSBTRole::UPDATER: return "updater"; case PSBTRole::SIGNER: return "signer"; case PSBTRole::FINALIZER: return "finalizer"; diff --git a/src/psbt.h b/src/psbt.h index bcff66f7a1..d507d5b6b7 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -560,6 +560,7 @@ struct PartiallySignedTransaction }; enum class PSBTRole { + CREATOR, UPDATER, SIGNER, FINALIZER, diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 10953adc35..21e51a380d 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -171,6 +171,7 @@ bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchS return false; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) { return false; } @@ -190,6 +191,7 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned cha bool fComp = ((vchSig[0] - 27) & 4) != 0; secp256k1_pubkey pubkey; secp256k1_ecdsa_recoverable_signature sig; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_verify, &sig, &vchSig[1], recid)) { return false; } @@ -207,6 +209,7 @@ bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; secp256k1_pubkey pubkey; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size()); } @@ -214,6 +217,7 @@ bool CPubKey::Decompress() { if (!IsValid()) return false; secp256k1_pubkey pubkey; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) { return false; } @@ -232,6 +236,7 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi BIP32Hash(cc, nChild, *begin(), begin()+1, out); memcpy(ccChild.begin(), out+32, 32); secp256k1_pubkey pubkey; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) { return false; } @@ -273,6 +278,7 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { /* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) { secp256k1_ecdsa_signature sig; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) { return false; } diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 48201b420e..b6c6984b10 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -8,7 +8,7 @@ #include <net_types.h> // For banmap_t #include <qt/clientmodel.h> -#include <algorithm> +#include <utility> #include <QDebug> #include <QList> diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2aeba6d82c..8444984b27 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -341,6 +341,7 @@ void BitcoinGUI::createActions() m_close_wallet_action->setStatusTip(tr("Close wallet")); m_create_wallet_action = new QAction(tr("Create Wallet..."), this); + m_create_wallet_action->setEnabled(false); m_create_wallet_action->setStatusTip(tr("Create a new wallet")); showHelpMessageAction = new QAction(tr("&Command-line options"), this); @@ -618,6 +619,7 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller) m_wallet_controller = wallet_controller; + m_create_wallet_action->setEnabled(true); m_open_wallet_action->setEnabled(true); m_open_wallet_action->setMenu(m_open_wallet_menu); diff --git a/src/qt/forms/openuridialog.ui b/src/qt/forms/openuridialog.ui index 2acec314fd..1b7291ab9d 100644 --- a/src/qt/forms/openuridialog.ui +++ b/src/qt/forms/openuridialog.ui @@ -24,7 +24,11 @@ </widget> </item> <item> - <widget class="QValidatedLineEdit" name="uriEdit"/> + <widget class="QValidatedLineEdit" name="uriEdit"> + <property name="placeholderText"> + <string notr="true">bitcoin:</string> + </property> + </widget> </item> </layout> </item> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 240a7a7e92..fea759dee0 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -685,6 +685,9 @@ <property name="toolTip"> <string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string> </property> + <property name="placeholderText"> + <string notr="true">https://example.com/tx/%s</string> + </property> </widget> </item> </layout> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 843d909f68..934363af1f 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -144,6 +144,9 @@ <property name="toolTip"> <string>Enter a label for this address to add it to the list of used addresses</string> </property> + <property name="placeholderText"> + <string>Enter a label for this address to add it to the list of used addresses</string> + </property> </widget> </item> <item row="2" column="0"> diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 202edf27d4..f42d19093b 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -121,6 +121,9 @@ </property> <item> <widget class="QLineEdit" name="signatureOut_SM"> + <property name="placeholderText"> + <string>Click "Sign Message" to generate signature</string> + </property> <property name="font"> <font> <italic>true</italic> diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 6a0143ac7e..9dc64bb23a 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -15,7 +15,6 @@ OpenURIDialog::OpenURIDialog(QWidget *parent) : ui(new Ui::OpenURIDialog) { ui->setupUi(this); - ui->uriEdit->setPlaceholderText("bitcoin:"); } OpenURIDialog::~OpenURIDialog() diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d48c537c75..3fffb8a288 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -77,9 +77,11 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->verticalLayout_Main->removeItem(ui->horizontalSpacer_0_Main); #endif - /* remove Wallet tab in case of -disablewallet */ + /* remove Wallet tab and 3rd party-URL textbox in case of -disablewallet */ if (!enableWallet) { ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet)); + ui->thirdPartyTxUrlsLabel->setVisible(false); + ui->thirdPartyTxUrls->setVisible(false); } /* Display elements init */ @@ -108,8 +110,6 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); } } - ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s"); - ui->unit->setModel(new BitcoinUnits(this)); /* Widget-to-option mapper */ diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index af2a1bb0e5..514ff35bcd 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -10,7 +10,7 @@ #include <interfaces/node.h> -#include <algorithm> +#include <utility> #include <QDebug> #include <QList> diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 6d933f46d6..18fa5f417f 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -12,8 +12,7 @@ #include <clientversion.h> #include <streams.h> -#include <algorithm> - +#include <utility> RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) @@ -214,10 +213,10 @@ void RecentRequestsTableModel::updateDisplayUnit() updateAmountColumnTitle(); } -bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const +bool RecentRequestEntryLessThan::operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const { - RecentRequestEntry *pLeft = &left; - RecentRequestEntry *pRight = &right; + const RecentRequestEntry* pLeft = &left; + const RecentRequestEntry* pRight = &right; if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 30578b7a03..f5085f7268 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -45,7 +45,7 @@ class RecentRequestEntryLessThan public: RecentRequestEntryLessThan(int nColumn, Qt::SortOrder fOrder): column(nColumn), order(fOrder) {} - bool operator()(RecentRequestEntry &left, RecentRequestEntry &right) const; + bool operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const; private: int column; diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index ad37e09114..610dfbb85a 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -37,7 +37,6 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par if (platformStyle->getUseExtraSpacing()) ui->payToLayout->setSpacing(4); - ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); // normal bitcoin address field GUIUtil::setupAddressWidget(ui->payTo, this); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 9d250bcb83..81e9d33a60 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -35,8 +35,6 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformS ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/transaction_0")); ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); - ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); - GUIUtil::setupAddressWidget(ui->addressIn_SM, this); GUIUtil::setupAddressWidget(ui->addressIn_VM, this); diff --git a/src/random.cpp b/src/random.cpp index 47d76d8700..99927fc5d2 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -6,6 +6,7 @@ #include <random.h> #include <compat/cpuid.h> +#include <crypto/sha256.h> #include <crypto/sha512.h> #include <support/cleanse.h> #ifdef WIN32 @@ -178,7 +179,7 @@ static uint64_t GetRdSeed() noexcept /* Access to other hardware random number generators could be added here later, * assuming it is sufficiently fast (in the order of a few hundred CPU cycles). * Slower sources should probably be invoked separately, and/or only from - * RandAddSeedSleep (which is called during idle background operation). + * RandAddPeriodic (which is called once a minute). */ static void InitHardwareRand() {} static void ReportHardwareRand() {} @@ -359,6 +360,9 @@ class RNGState { uint64_t m_counter GUARDED_BY(m_mutex) = 0; bool m_strongly_seeded GUARDED_BY(m_mutex) = false; + Mutex m_events_mutex; + CSHA256 m_events_hasher GUARDED_BY(m_events_mutex); + public: RNGState() noexcept { @@ -369,6 +373,35 @@ public: { } + void AddEvent(uint32_t event_info) noexcept + { + LOCK(m_events_mutex); + + m_events_hasher.Write((const unsigned char *)&event_info, sizeof(event_info)); + // Get the low four bytes of the performance counter. This translates to roughly the + // subsecond part. + uint32_t perfcounter = (GetPerformanceCounter() & 0xffffffff); + m_events_hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter)); + } + + /** + * Feed (the hash of) all events added through AddEvent() to hasher. + */ + void SeedEvents(CSHA512& hasher) noexcept + { + // We use only SHA256 for the events hashing to get the ASM speedups we have for SHA256, + // since we want it to be fast as network peers may be able to trigger it repeatedly. + LOCK(m_events_mutex); + + unsigned char events_hash[32]; + m_events_hasher.Finalize(events_hash); + hasher.Write(events_hash, 32); + + // Re-initialize the hasher with the finalized state to use later. + m_events_hasher.Reset(); + m_events_hasher.Write(events_hash, 32); + } + /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. * * If this function has never been called with strong_seed = true, false is returned. @@ -415,17 +448,7 @@ RNGState& GetRNGState() noexcept /* A note on the use of noexcept in the seeding functions below: * - * None of the RNG code should ever throw any exception, with the sole exception - * of MilliSleep in SeedSleep, which can (and does) support interruptions which - * cause a boost::thread_interrupted to be thrown. - * - * This means that SeedSleep, and all functions that invoke it are throwing. - * However, we know that GetRandBytes() and GetStrongRandBytes() never trigger - * this sleeping logic, so they are noexcept. The same is true for all the - * GetRand*() functions that use GetRandBytes() indirectly. - * - * TODO: After moving away from interruptible boost-based thread management, - * everything can become noexcept here. + * None of the RNG code should ever throw any exception. */ static void SeedTimestamp(CSHA512& hasher) noexcept @@ -449,7 +472,7 @@ static void SeedFast(CSHA512& hasher) noexcept SeedTimestamp(hasher); } -static void SeedSlow(CSHA512& hasher) noexcept +static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept { unsigned char buffer[32]; @@ -460,6 +483,9 @@ static void SeedSlow(CSHA512& hasher) noexcept GetOSRand(buffer); hasher.Write(buffer, sizeof(buffer)); + // Add the events hasher into the mix + rng.SeedEvents(hasher); + // High-precision timestamp. // // Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a @@ -477,7 +503,7 @@ static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noe Strengthen(strengthen_seed, microseconds, hasher); } -static void SeedPeriodic(CSHA512& hasher, RNGState& rng) +static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept { // Everything that the 'fast' seeder includes SeedFast(hasher); @@ -485,10 +511,13 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) // High-precision timestamp SeedTimestamp(hasher); + // Add the events hasher into the mix + rng.SeedEvents(hasher); + // Dynamic environment data (performance monitoring, ...) auto old_size = hasher.Size(); RandAddDynamicEnv(hasher); - LogPrintf("Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); + LogPrint(BCLog::RAND, "Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 10 ms SeedStrengthen(hasher, rng, 10000); @@ -500,7 +529,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept SeedHardwareSlow(hasher); // Everything that the 'slow' seeder includes. - SeedSlow(hasher); + SeedSlow(hasher, rng); // Dynamic environment data (performance monitoring, ...) auto old_size = hasher.Size(); @@ -508,7 +537,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept // Static environment data RandAddStaticEnv(hasher); - LogPrintf("Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); + LogPrint(BCLog::RAND, "Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 100 ms SeedStrengthen(hasher, rng, 100000); @@ -520,7 +549,7 @@ enum class RNGLevel { PERIODIC, //!< Called by RandAddPeriodic() }; -static void ProcRand(unsigned char* out, int num, RNGLevel level) +static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept { // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available). RNGState& rng = GetRNGState(); @@ -533,7 +562,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) SeedFast(hasher); break; case RNGLevel::SLOW: - SeedSlow(hasher); + SeedSlow(hasher, rng); break; case RNGLevel::PERIODIC: SeedPeriodic(hasher, rng); @@ -551,7 +580,8 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); } void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); } -void RandAddPeriodic() { ProcRand(nullptr, 0, RNGLevel::PERIODIC); } +void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); } +void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(event_info); } bool g_mock_deterministic_tests{false}; diff --git a/src/random.h b/src/random.h index 2d8ab085e3..e1b105168d 100644 --- a/src/random.h +++ b/src/random.h @@ -40,17 +40,17 @@ * These entropy sources are slower, but designed to make sure the RNG state contains * fresh data that is unpredictable to attackers. * - * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally: - * - A high-precision timestamp before and after sleeping 1ms. - * - (On Windows) Once every 10 minutes, performance monitoring data from the OS. - - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512. - * These just exploit the fact the system is idle to improve the quality of the RNG - * slightly. + * - RandAddPeriodic() seeds everything that fast seeding includes, but additionally: + * - A high-precision timestamp + * - Dynamic environment data (performance monitoring, ...) + * - Strengthen the entropy for 10 ms using repeated SHA512. + * This is run once every minute. * * On first use of the RNG (regardless of what function is called first), all entropy * sources used in the 'slow' seeder are included, but also: * - 256 bits from the hardware RNG (rdseed or rdrand) when available. - * - (On Windows) Performance monitoring data from the OS. + * - Dynamic environment data (performance monitoring, ...) + * - Static environment data * - Strengthen the entropy for 100 ms using repeated SHA512. * * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and @@ -87,7 +87,15 @@ void GetStrongRandBytes(unsigned char* buf, int num) noexcept; * * Thread-safe. */ -void RandAddPeriodic(); +void RandAddPeriodic() noexcept; + +/** + * Gathers entropy from the low bits of the time at which events occur. Should + * be called with a uint32_t describing the event at the time an event occurs. + * + * Thread-safe. + */ +void RandAddEvent(const uint32_t event_info) noexcept; /** * Fast randomness source. This is seeded once with secure random data, but diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 6f24caee21..6a02984a0e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1674,6 +1674,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request) " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n" " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n" " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n" + " \"error\" : \"error\" (string) Error message if there is one" "}\n" }, RPCExamples { @@ -1726,7 +1727,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request) } inputs_result.push_back(input_univ); } - result.pushKV("inputs", inputs_result); + if (!inputs_result.empty()) result.pushKV("inputs", inputs_result); if (psbta.estimated_vsize != nullopt) { result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize); @@ -1738,6 +1739,9 @@ UniValue analyzepsbt(const JSONRPCRequest& request) result.pushKV("fee", ValueFromAmount(*psbta.fee)); } result.pushKV("next", PSBTRoleName(psbta.next)); + if (!psbta.error.empty()) { + result.pushKV("error", psbta.error); + } return result; } diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 6745bb9015..482fe3772c 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -17,6 +17,7 @@ #include <condition_variable> #include <unordered_set> +#include <utility> BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 591a317d17..2deb0c5bfc 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -186,14 +186,15 @@ static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &sa BOOST_CHECK(HexStr(out, out + 32) == okm_check_hex); } -static std::string LongTestString() { +static std::string LongTestString() +{ std::string ret; - for (int i=0; i<200000; i++) { - ret += (unsigned char)(i); - ret += (unsigned char)(i >> 4); - ret += (unsigned char)(i >> 8); - ret += (unsigned char)(i >> 12); - ret += (unsigned char)(i >> 16); + for (int i = 0; i < 200000; i++) { + ret += (char)(i); + ret += (char)(i >> 4); + ret += (char)(i >> 8); + ret += (char)(i >> 12); + ret += (char)(i >> 16); } return ret; } diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 57d5b2bb5c..b647c0f70b 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -397,6 +397,18 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) } } +BOOST_AUTO_TEST_CASE(unicodepath) +{ + // Attempt to create a database with a utf8 character in the path. + // On Windows this test will fail if the directory is created using + // the ANSI CreateDirectoryA call and the code page isn't UTF8. + // It will succeed if the created with CreateDirectoryW. + fs::path ph = GetDataDir() / "test_runner_₿_🏃_20191128_104644"; + CDBWrapper dbw(ph, (1 << 20)); + + fs::path lockPath = ph / "LOCK"; + BOOST_CHECK(boost::filesystem::exists(lockPath)); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index c4c25854fd..47d5038c26 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -3,11 +3,14 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> +#include <pubkey.h> #include <script/descriptor.h> #include <test/fuzz/fuzz.h> +#include <util/memory.h> void initialize() { + static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); SelectParams(CBaseChainParams::REGTEST); } diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp index bcd8691359..bd05283b78 100644 --- a/src/test/fuzz/deserialize.cpp +++ b/src/test/fuzz/deserialize.cpp @@ -5,18 +5,25 @@ #include <addrdb.h> #include <addrman.h> #include <blockencodings.h> +#include <blockfilter.h> #include <chain.h> #include <coins.h> #include <compressor.h> #include <consensus/merkle.h> +#include <key.h> +#include <merkleblock.h> #include <net.h> #include <primitives/block.h> #include <protocol.h> +#include <psbt.h> #include <pubkey.h> +#include <script/keyorigin.h> #include <streams.h> #include <undo.h> #include <version.h> +#include <exception> +#include <stdexcept> #include <stdint.h> #include <unistd.h> @@ -30,137 +37,186 @@ void initialize() static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +namespace { + +struct invalid_fuzzing_input_exception : public std::exception { +}; + +template <typename T> +CDataStream Serialize(const T& obj) +{ + CDataStream ds(SER_NETWORK, INIT_PROTO_VERSION); + ds << obj; + return ds; +} + +template <typename T> +T Deserialize(CDataStream ds) +{ + T obj; + ds >> obj; + return obj; +} + +template <typename T> +void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { - int nVersion; - ds >> nVersion; - ds.SetVersion(nVersion); - } catch (const std::ios_base::failure& e) { - return; + int version; + ds >> version; + ds.SetVersion(version); + } catch (const std::ios_base::failure&) { + throw invalid_fuzzing_input_exception(); + } + try { + ds >> obj; + } catch (const std::ios_base::failure&) { + throw invalid_fuzzing_input_exception(); } + assert(buffer.empty() || !Serialize(obj).empty()); +} + +template <typename T> +void AssertEqualAfterSerializeDeserialize(const T& obj) +{ + assert(Deserialize<T>(Serialize(obj)) == obj); +} -#if BLOCK_DESERIALIZE - try - { - CBlock block; - ds >> block; - } catch (const std::ios_base::failure& e) {return;} +} // namespace + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + try { +#if BLOCK_FILTER_DESERIALIZE + BlockFilter block_filter; + DeserializeFromFuzzingInput(buffer, block_filter); +#elif ADDR_INFO_DESERIALIZE + CAddrInfo addr_info; + DeserializeFromFuzzingInput(buffer, addr_info); +#elif BLOCK_FILE_INFO_DESERIALIZE + CBlockFileInfo block_file_info; + DeserializeFromFuzzingInput(buffer, block_file_info); +#elif BLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE + CBlockHeaderAndShortTxIDs block_header_and_short_txids; + DeserializeFromFuzzingInput(buffer, block_header_and_short_txids); +#elif FEE_RATE_DESERIALIZE + CFeeRate fee_rate; + DeserializeFromFuzzingInput(buffer, fee_rate); + AssertEqualAfterSerializeDeserialize(fee_rate); +#elif MERKLE_BLOCK_DESERIALIZE + CMerkleBlock merkle_block; + DeserializeFromFuzzingInput(buffer, merkle_block); +#elif OUT_POINT_DESERIALIZE + COutPoint out_point; + DeserializeFromFuzzingInput(buffer, out_point); + AssertEqualAfterSerializeDeserialize(out_point); +#elif PARTIAL_MERKLE_TREE_DESERIALIZE + CPartialMerkleTree partial_merkle_tree; + DeserializeFromFuzzingInput(buffer, partial_merkle_tree); +#elif PUB_KEY_DESERIALIZE + CPubKey pub_key; + DeserializeFromFuzzingInput(buffer, pub_key); + // TODO: The following equivalence should hold for CPubKey? Fix. + // AssertEqualAfterSerializeDeserialize(pub_key); +#elif SCRIPT_DESERIALIZE + CScript script; + DeserializeFromFuzzingInput(buffer, script); +#elif SUB_NET_DESERIALIZE + CSubNet sub_net; + DeserializeFromFuzzingInput(buffer, sub_net); + AssertEqualAfterSerializeDeserialize(sub_net); +#elif TX_IN_DESERIALIZE + CTxIn tx_in; + DeserializeFromFuzzingInput(buffer, tx_in); + AssertEqualAfterSerializeDeserialize(tx_in); +#elif FLAT_FILE_POS_DESERIALIZE + FlatFilePos flat_file_pos; + DeserializeFromFuzzingInput(buffer, flat_file_pos); + AssertEqualAfterSerializeDeserialize(flat_file_pos); +#elif KEY_ORIGIN_INFO_DESERIALIZE + KeyOriginInfo key_origin_info; + DeserializeFromFuzzingInput(buffer, key_origin_info); + AssertEqualAfterSerializeDeserialize(key_origin_info); +#elif PARTIALLY_SIGNED_TRANSACTION_DESERIALIZE + PartiallySignedTransaction partially_signed_transaction; + DeserializeFromFuzzingInput(buffer, partially_signed_transaction); +#elif PREFILLED_TRANSACTION_DESERIALIZE + PrefilledTransaction prefilled_transaction; + DeserializeFromFuzzingInput(buffer, prefilled_transaction); +#elif PSBT_INPUT_DESERIALIZE + PSBTInput psbt_input; + DeserializeFromFuzzingInput(buffer, psbt_input); +#elif PSBT_OUTPUT_DESERIALIZE + PSBTOutput psbt_output; + DeserializeFromFuzzingInput(buffer, psbt_output); +#elif BLOCK_DESERIALIZE + CBlock block; + DeserializeFromFuzzingInput(buffer, block); #elif BLOCKLOCATOR_DESERIALIZE - try - { - CBlockLocator bl; - ds >> bl; - } catch (const std::ios_base::failure& e) {return;} + CBlockLocator bl; + DeserializeFromFuzzingInput(buffer, bl); #elif BLOCKMERKLEROOT - try - { - CBlock block; - ds >> block; - bool mutated; - BlockMerkleRoot(block, &mutated); - } catch (const std::ios_base::failure& e) {return;} + CBlock block; + DeserializeFromFuzzingInput(buffer, block); + bool mutated; + BlockMerkleRoot(block, &mutated); #elif ADDRMAN_DESERIALIZE - try - { - CAddrMan am; - ds >> am; - } catch (const std::ios_base::failure& e) {return;} + CAddrMan am; + DeserializeFromFuzzingInput(buffer, am); #elif BLOCKHEADER_DESERIALIZE - try - { - CBlockHeader bh; - ds >> bh; - } catch (const std::ios_base::failure& e) {return;} + CBlockHeader bh; + DeserializeFromFuzzingInput(buffer, bh); #elif BANENTRY_DESERIALIZE - try - { - CBanEntry be; - ds >> be; - } catch (const std::ios_base::failure& e) {return;} + CBanEntry be; + DeserializeFromFuzzingInput(buffer, be); #elif TXUNDO_DESERIALIZE - try - { - CTxUndo tu; - ds >> tu; - } catch (const std::ios_base::failure& e) {return;} + CTxUndo tu; + DeserializeFromFuzzingInput(buffer, tu); #elif BLOCKUNDO_DESERIALIZE - try - { - CBlockUndo bu; - ds >> bu; - } catch (const std::ios_base::failure& e) {return;} + CBlockUndo bu; + DeserializeFromFuzzingInput(buffer, bu); #elif COINS_DESERIALIZE - try - { - Coin coin; - ds >> coin; - } catch (const std::ios_base::failure& e) {return;} + Coin coin; + DeserializeFromFuzzingInput(buffer, coin); #elif NETADDR_DESERIALIZE - try - { - CNetAddr na; - ds >> na; - } catch (const std::ios_base::failure& e) {return;} + CNetAddr na; + DeserializeFromFuzzingInput(buffer, na); + AssertEqualAfterSerializeDeserialize(na); #elif SERVICE_DESERIALIZE - try - { - CService s; - ds >> s; - } catch (const std::ios_base::failure& e) {return;} + CService s; + DeserializeFromFuzzingInput(buffer, s); + AssertEqualAfterSerializeDeserialize(s); #elif MESSAGEHEADER_DESERIALIZE - CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; - try - { - CMessageHeader mh(pchMessageStart); - ds >> mh; - if (!mh.IsValid(pchMessageStart)) {return;} - } catch (const std::ios_base::failure& e) {return;} + const CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; + CMessageHeader mh(pchMessageStart); + DeserializeFromFuzzingInput(buffer, mh); + (void)mh.IsValid(pchMessageStart); #elif ADDRESS_DESERIALIZE - try - { - CAddress a; - ds >> a; - } catch (const std::ios_base::failure& e) {return;} + CAddress a; + DeserializeFromFuzzingInput(buffer, a); #elif INV_DESERIALIZE - try - { - CInv i; - ds >> i; - } catch (const std::ios_base::failure& e) {return;} + CInv i; + DeserializeFromFuzzingInput(buffer, i); #elif BLOOMFILTER_DESERIALIZE - try - { - CBloomFilter bf; - ds >> bf; - } catch (const std::ios_base::failure& e) {return;} + CBloomFilter bf; + DeserializeFromFuzzingInput(buffer, bf); #elif DISKBLOCKINDEX_DESERIALIZE - try - { - CDiskBlockIndex dbi; - ds >> dbi; - } catch (const std::ios_base::failure& e) {return;} + CDiskBlockIndex dbi; + DeserializeFromFuzzingInput(buffer, dbi); #elif TXOUTCOMPRESSOR_DESERIALIZE - CTxOut to; - CTxOutCompressor toc(to); - try - { - ds >> toc; - } catch (const std::ios_base::failure& e) {return;} + CTxOut to; + CTxOutCompressor toc(to); + DeserializeFromFuzzingInput(buffer, toc); #elif BLOCKTRANSACTIONS_DESERIALIZE - try - { - BlockTransactions bt; - ds >> bt; - } catch (const std::ios_base::failure& e) {return;} + BlockTransactions bt; + DeserializeFromFuzzingInput(buffer, bt); #elif BLOCKTRANSACTIONSREQUEST_DESERIALIZE - try - { - BlockTransactionsRequest btr; - ds >> btr; - } catch (const std::ios_base::failure& e) {return;} + BlockTransactionsRequest btr; + DeserializeFromFuzzingInput(buffer, btr); #else #error Need at least one fuzz target to compile #endif + } catch (const invalid_fuzzing_input_exception&) { + } } diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp new file mode 100644 index 0000000000..723938bcdb --- /dev/null +++ b/src/test/fuzz/integer.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <arith_uint256.h> +#include <compressor.h> +#include <consensus/merkle.h> +#include <core_io.h> +#include <crypto/common.h> +#include <crypto/siphash.h> +#include <key_io.h> +#include <memusage.h> +#include <netbase.h> +#include <policy/settings.h> +#include <pow.h> +#include <pubkey.h> +#include <rpc/util.h> +#include <script/signingprovider.h> +#include <script/standard.h> +#include <serialize.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <uint256.h> +#include <util/strencodings.h> +#include <util/system.h> +#include <util/time.h> + +#include <cassert> +#include <limits> +#include <vector> + +void initialize() +{ + SelectParams(CBaseChainParams::REGTEST); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + if (buffer.size() < sizeof(uint256) + sizeof(uint160)) { + return; + } + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const uint256 u256(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256))); + const uint160 u160(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint160))); + const uint64_t u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); + const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + const uint32_t u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + const int32_t i32 = fuzzed_data_provider.ConsumeIntegral<int32_t>(); + const uint16_t u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>(); + const int16_t i16 = fuzzed_data_provider.ConsumeIntegral<int16_t>(); + const uint8_t u8 = fuzzed_data_provider.ConsumeIntegral<uint8_t>(); + const int8_t i8 = fuzzed_data_provider.ConsumeIntegral<int8_t>(); + // We cannot assume a specific value of std::is_signed<char>::value: + // ConsumeIntegral<char>() instead of casting from {u,}int8_t. + const char ch = fuzzed_data_provider.ConsumeIntegral<char>(); + + const Consensus::Params& consensus_params = Params().GetConsensus(); + (void)CheckProofOfWork(u256, u32, consensus_params); + (void)CompressAmount(u64); + static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000")); + static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + const std::vector<uint256> v256{u256, u256_min, u256_max}; + (void)ComputeMerkleRoot(v256); + (void)CountBits(u64); + (void)DecompressAmount(u64); + (void)FormatISO8601Date(i64); + (void)FormatISO8601DateTime(i64); + (void)GetSizeOfCompactSize(u64); + (void)GetSpecialScriptSize(u32); + // (void)GetVirtualTransactionSize(i64, i64); // function defined only for a subset of int64_t inputs + // (void)GetVirtualTransactionSize(i64, i64, u32); // function defined only for a subset of int64_t/uint32_t inputs + (void)HexDigit(ch); + (void)i64tostr(i64); + (void)IsDigit(ch); + (void)IsSpace(ch); + (void)IsSwitchChar(ch); + (void)itostr(i32); + (void)memusage::DynamicUsage(ch); + (void)memusage::DynamicUsage(i16); + (void)memusage::DynamicUsage(i32); + (void)memusage::DynamicUsage(i64); + (void)memusage::DynamicUsage(i8); + (void)memusage::DynamicUsage(u16); + (void)memusage::DynamicUsage(u32); + (void)memusage::DynamicUsage(u64); + (void)memusage::DynamicUsage(u8); + const unsigned char uch = static_cast<unsigned char>(u8); + (void)memusage::DynamicUsage(uch); + (void)MillisToTimeval(i64); + const double d = ser_uint64_to_double(u64); + assert(ser_double_to_uint64(d) == u64); + const float f = ser_uint32_to_float(u32); + assert(ser_float_to_uint32(f) == u32); + (void)SighashToStr(uch); + (void)SipHashUint256(u64, u64, u256); + (void)SipHashUint256Extra(u64, u64, u256, u32); + (void)ToLower(ch); + + const arith_uint256 au256 = UintToArith256(u256); + assert(ArithToUint256(au256) == u256); + assert(uint256S(au256.GetHex()) == u256); + (void)au256.bits(); + (void)au256.GetCompact(/* fNegative= */ false); + (void)au256.GetCompact(/* fNegative= */ true); + (void)au256.getdouble(); + (void)au256.GetHex(); + (void)au256.GetLow64(); + (void)au256.size(); + (void)au256.ToString(); + + const CKeyID key_id{u160}; + const CScriptID script_id{u160}; + // CTxDestination = CNoDestination ∪ PKHash ∪ ScriptHash ∪ WitnessV0ScriptHash ∪ WitnessV0KeyHash ∪ WitnessUnknown + const PKHash pk_hash{u160}; + const ScriptHash script_hash{u160}; + const WitnessV0KeyHash witness_v0_key_hash{u160}; + const WitnessV0ScriptHash witness_v0_script_hash{u256}; + const std::vector<CTxDestination> destinations{pk_hash, script_hash, witness_v0_key_hash, witness_v0_script_hash}; + const SigningProvider store; + for (const CTxDestination& destination : destinations) { + (void)DescribeAddress(destination); + (void)EncodeDestination(destination); + (void)GetKeyForDestination(store, destination); + (void)GetScriptForDestination(destination); + (void)IsValidDestination(destination); + } +} diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index 76b230ef3c..fefafda36b 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -26,19 +26,31 @@ void test_one_input(const std::vector<uint8_t>& buffer) int nVersion; ds >> nVersion; ds.SetVersion(nVersion); - } catch (const std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { return; } - bool valid = true; + bool valid_tx = true; const CTransaction tx = [&] { try { return CTransaction(deserialize, ds); - } catch (const std::ios_base::failure& e) { - valid = false; + } catch (const std::ios_base::failure&) { + valid_tx = false; return CTransaction(); } }(); - if (!valid) { + bool valid_mutable_tx = true; + CDataStream ds_mtx(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CMutableTransaction mutable_tx; + try { + int nVersion; + ds_mtx >> nVersion; + ds_mtx.SetVersion(nVersion); + ds_mtx >> mutable_tx; + } catch (const std::ios_base::failure&) { + valid_mutable_tx = false; + } + assert(valid_tx == valid_mutable_tx); + if (!valid_tx) { return; } diff --git a/src/test/fuzz/tx_in.cpp b/src/test/fuzz/tx_in.cpp new file mode 100644 index 0000000000..8e116537d1 --- /dev/null +++ b/src/test/fuzz/tx_in.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <core_memusage.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <streams.h> +#include <test/fuzz/fuzz.h> +#include <version.h> + +#include <cassert> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CTxIn tx_in; + try { + int version; + ds >> version; + ds.SetVersion(version); + ds >> tx_in; + } catch (const std::ios_base::failure&) { + return; + } + + (void)GetTransactionInputWeight(tx_in); + (void)GetVirtualTransactionInputSize(tx_in); + (void)RecursiveDynamicUsage(tx_in); + + (void)tx_in.ToString(); +} diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp new file mode 100644 index 0000000000..aa1338d5ba --- /dev/null +++ b/src/test/fuzz/tx_out.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <core_memusage.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <streams.h> +#include <test/fuzz/fuzz.h> +#include <version.h> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CTxOut tx_out; + try { + int version; + ds >> version; + ds.SetVersion(version); + ds >> tx_out; + } catch (const std::ios_base::failure&) { + return; + } + + const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE}; + (void)GetDustThreshold(tx_out, dust_relay_fee); + (void)IsDust(tx_out, dust_relay_fee); + (void)RecursiveDynamicUsage(tx_out); + + (void)tx_out.ToString(); + (void)tx_out.IsNull(); + tx_out.SetNull(); + assert(tx_out.IsNull()); +} diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index eb0050a4a3..0939803953 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -713,6 +713,29 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].nValue = nDustThreshold; BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + // Disallowed nVersion + t.nVersion = -1; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "version"); + + t.nVersion = 0; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "version"); + + t.nVersion = 3; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "version"); + + // Allowed nVersion + t.nVersion = 1; + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + + t.nVersion = 2; + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + // Check dust with odd relay fee to verify rounding: // nDustThreshold = 182 * 3702 / 1000 dustRelayFee = CFeeRate(3702); @@ -797,6 +820,17 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) reason.clear(); BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); BOOST_CHECK_EQUAL(reason, "scriptsig-size"); + + // Check bare multisig (standard if policy flag fIsBareMultisigStd is set) + fIsBareMultisigStd = true; + t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1 + t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0); + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + + fIsBareMultisigStd = false; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "bare-multisig"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tinyformat.h b/src/tinyformat.h index 182f518a0b..be63f2d5d8 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -33,6 +33,7 @@ // // * Type safety and extensibility for user defined types. // * C99 printf() compatibility, to the extent possible using std::ostream +// * POSIX extension for positional arguments // * Simplicity and minimalism. A single header file to include and distribute // with your projects. // * Augment rather than replace the standard stream formatting mechanism @@ -42,7 +43,7 @@ // Main interface example usage // ---------------------------- // -// To print a date to std::cout: +// To print a date to std::cout for American usage: // // std::string weekday = "Wednesday"; // const char* month = "July"; @@ -52,6 +53,14 @@ // // tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); // +// POSIX extension for positional arguments is available. +// The ability to rearrange formatting arguments is an important feature +// for localization because the word order may vary in different languages. +// +// Previous example for German usage. Arguments are reordered: +// +// tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min); +// // The strange types here emphasize the type safety of the interface; it is // possible to print a std::string using the "%s" conversion, and a // size_t using the "%d" conversion. A similar result could be achieved @@ -133,12 +142,17 @@ namespace tfm = tinyformat; //------------------------------------------------------------------------------ // Implementation details. #include <algorithm> -#include <cassert> #include <iostream> #include <sstream> -#include <stdexcept> +#include <stdexcept> // Added for Bitcoin Core + +#ifndef TINYFORMAT_ASSERT +# include <cassert> +# define TINYFORMAT_ASSERT(cond) assert(cond) +#endif #ifndef TINYFORMAT_ERROR +# include <cassert> # define TINYFORMAT_ERROR(reason) assert(0 && reason) #endif @@ -149,13 +163,13 @@ namespace tfm = tinyformat; #endif #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 -// std::showpos is broken on old libstdc++ as provided with OSX. See +// std::showpos is broken on old libstdc++ as provided with macOS. See // http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #endif #ifdef __APPLE__ -// Workaround OSX linker warning: Xcode uses different default symbol +// Workaround macOS linker warning: Xcode uses different default symbol // visibilities for static libs vs executables (see issue #25) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) #else @@ -164,6 +178,7 @@ namespace tfm = tinyformat; namespace tinyformat { +// Added for Bitcoin Core class format_error: public std::runtime_error { public: @@ -218,7 +233,7 @@ template<int n> struct is_wchar<wchar_t[n]> {}; template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value> struct formatValueAsType { - static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } + static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); } }; // Specialized version for types that can actually be converted to fmtT, as // indicated by the "convertible" template parameter. @@ -240,8 +255,7 @@ struct formatZeroIntegerWorkaround<T,true> { static bool invoke(std::ostream& out, const T& value) { - if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) - { + if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) { out << "+0"; return true; } @@ -282,7 +296,7 @@ inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ { \ std::streamsize len = 0; \ - while(len < ntrunc && value[len] != 0) \ + while (len < ntrunc && value[len] != 0) \ ++len; \ out.write(value, len); \ } @@ -328,15 +342,14 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, // could otherwise lead to a crash when printing a dangling (const char*). const bool canConvertToChar = detail::is_convertible<T,char>::value; const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value; - if(canConvertToChar && *(fmtEnd-1) == 'c') + if (canConvertToChar && *(fmtEnd-1) == 'c') detail::formatValueAsType<T, char>::invoke(out, value); - else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') + else if (canConvertToVoidPtr && *(fmtEnd-1) == 'p') detail::formatValueAsType<T, const void*>::invoke(out, value); #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; + else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; #endif - else if(ntrunc >= 0) - { + else if (ntrunc >= 0) { // Take care not to overread C strings in truncating conversions like // "%.4s" where at most 4 characters may be read. detail::formatTruncated(out, value, ntrunc); @@ -351,8 +364,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ const char* fmtEnd, int /**/, charType value) \ { \ - switch(*(fmtEnd-1)) \ - { \ + switch (*(fmtEnd-1)) { \ case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ out << static_cast<int>(value); break; \ default: \ @@ -490,19 +502,19 @@ namespace detail { // Type-opaque holder for an argument to format(), with associated actions on // the type held as explicit function pointers. This allows FormatArg's for -// each argument to be allocated as a homogenous array inside FormatList +// each argument to be allocated as a homogeneous array inside FormatList // whereas a naive implementation based on inheritance does not. class FormatArg { public: FormatArg() - : m_value(nullptr), - m_formatImpl(nullptr), - m_toIntImpl(nullptr) - { } + : m_value(NULL), + m_formatImpl(NULL), + m_toIntImpl(NULL) + { } template<typename T> - explicit FormatArg(const T& value) + FormatArg(const T& value) : m_value(static_cast<const void*>(&value)), m_formatImpl(&formatImpl<T>), m_toIntImpl(&toIntImpl<T>) @@ -511,15 +523,15 @@ class FormatArg void format(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc) const { - assert(m_value); - assert(m_formatImpl); + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_formatImpl); m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } int toInt() const { - assert(m_value); - assert(m_toIntImpl); + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_toIntImpl); return m_toIntImpl(m_value); } @@ -549,36 +561,68 @@ class FormatArg inline int parseIntAndAdvance(const char*& c) { int i = 0; - for(;*c >= '0' && *c <= '9'; ++c) + for (;*c >= '0' && *c <= '9'; ++c) i = 10*i + (*c - '0'); return i; } -// Print literal part of format string and return next format spec -// position. +// Parse width or precision `n` from format string pointer `c`, and advance it +// to the next character. If an indirection is requested with `*`, the argument +// is read from `args[argIndex]` and `argIndex` is incremented (or read +// from `args[n]` in positional mode). Returns true if one or more +// characters were read. +inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode, + const detail::FormatArg* args, + int& argIndex, int numArgs) +{ + if (*c >= '0' && *c <= '9') { + n = parseIntAndAdvance(c); + } + else if (*c == '*') { + ++c; + n = 0; + if (positionalMode) { + int pos = parseIntAndAdvance(c) - 1; + if (*c != '$') + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + if (pos >= 0 && pos < numArgs) + n = args[pos].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Positional argument out of range"); + ++c; + } + else { + if (argIndex < numArgs) + n = args[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision"); + } + } + else { + return false; + } + return true; +} + +// Print literal part of format string and return next format spec position. // -// Skips over any occurrences of '%%', printing a literal '%' to the -// output. The position of the first % character of the next -// nontrivial format spec is returned, or the end of string. +// Skips over any occurrences of '%%', printing a literal '%' to the output. +// The position of the first % character of the next nontrivial format spec is +// returned, or the end of string. inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) { const char* c = fmt; - for(;; ++c) - { - switch(*c) - { - case '\0': - out.write(fmt, c - fmt); + for (;; ++c) { + if (*c == '\0') { + out.write(fmt, c - fmt); + return c; + } + else if (*c == '%') { + out.write(fmt, c - fmt); + if (*(c+1) != '%') return c; - case '%': - out.write(fmt, c - fmt); - if(*(c+1) != '%') - return c; - // for "%%", tack trailing % onto next literal section. - fmt = ++c; - break; - default: - break; + // for "%%", tack trailing % onto next literal section. + fmt = ++c; } } } @@ -587,23 +631,43 @@ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) // Parse a format string and set the stream state accordingly. // // The format mini-language recognized here is meant to be the one from C99, -// with the form "%[flags][width][.precision][length]type". +// with the form "%[flags][width][.precision][length]type" with POSIX +// positional arguments extension. +// +// POSIX positional arguments extension: +// Conversions can be applied to the nth argument after the format in +// the argument list, rather than to the next unused argument. In this case, +// the conversion specifier character % (see below) is replaced by the sequence +// "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}], +// giving the position of the argument in the argument list. This feature +// provides for the definition of format strings that select arguments +// in an order appropriate to specific languages. +// +// The format can contain either numbered argument conversion specifications +// (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications +// (that is, % and * ), but not both. The only exception to this is that %% +// can be mixed with the "%n$" form. The results of mixing numbered and +// unnumbered argument specifications in a format string are undefined. +// When numbered argument specifications are used, specifying the Nth argument +// requires that all the leading arguments, from the first to the (N-1)th, +// are specified in the format string. +// +// In format strings containing the "%n$" form of conversion specification, +// numbered arguments in the argument list can be referenced from the format +// string as many times as required. // // Formatting options which can't be natively represented using the ostream // state are returned in spacePadPositive (for space padded positive numbers) // and ntrunc (for truncating conversions). argIndex is incremented if // necessary to pull out variable width and precision. The function returns a // pointer to the character after the end of the current format spec. -inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, +inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode, + bool& spacePadPositive, int& ntrunc, const char* fmtStart, - const detail::FormatArg* formatters, - int& argIndex, int numFormatters) + const detail::FormatArg* args, + int& argIndex, int numArgs) { - if(*fmtStart != '%') - { - TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); - return fmtStart; - } + TINYFORMAT_ASSERT(*fmtStart == '%'); // Reset stream state to defaults. out.width(0); out.precision(6); @@ -616,100 +680,113 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi bool widthSet = false; int widthExtra = 0; const char* c = fmtStart + 1; - // 1) Parse flags - for(;; ++c) - { - switch(*c) - { - case '#': - out.setf(std::ios::showpoint | std::ios::showbase); - continue; - case '0': - // overridden by left alignment ('-' flag) - if(!(out.flags() & std::ios::left)) - { - // Use internal padding so that numeric values are - // formatted correctly, eg -00010 rather than 000-10 - out.fill('0'); - out.setf(std::ios::internal, std::ios::adjustfield); - } - continue; - case '-': - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - continue; - case ' ': - // overridden by show positive sign, '+' flag. - if(!(out.flags() & std::ios::showpos)) - spacePadPositive = true; - continue; - case '+': - out.setf(std::ios::showpos); - spacePadPositive = false; - widthExtra = 1; - continue; - default: - break; + + // 1) Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag. + if (*c >= '0' && *c <= '9') { + const char tmpc = *c; + int value = parseIntAndAdvance(c); + if (*c == '$') { + // value is an argument index + if (value > 0 && value <= numArgs) + argIndex = value - 1; + else + TINYFORMAT_ERROR("tinyformat: Positional argument out of range"); + ++c; + positionalMode = true; + } + else if (positionalMode) { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + } + else { + if (tmpc == '0') { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + if (value != 0) { + // Nonzero value means that we parsed width. + widthSet = true; + out.width(value); + } } - break; } - // 2) Parse width - if(*c >= '0' && *c <= '9') - { - widthSet = true; - out.width(parseIntAndAdvance(c)); + else if (positionalMode) { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); } - if(*c == '*') - { - widthSet = true; + // 2) Parse flags and width if we did not do it in previous step. + if (!widthSet) { + // Parse flags + for (;; ++c) { + switch (*c) { + case '#': + out.setf(std::ios::showpoint | std::ios::showbase); + continue; + case '0': + // overridden by left alignment ('-' flag) + if (!(out.flags() & std::ios::left)) { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + continue; + case '-': + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + continue; + case ' ': + // overridden by show positive sign, '+' flag. + if (!(out.flags() & std::ios::showpos)) + spacePadPositive = true; + continue; + case '+': + out.setf(std::ios::showpos); + spacePadPositive = false; + widthExtra = 1; + continue; + default: + break; + } + break; + } + // Parse width int width = 0; - if(argIndex < numFormatters) - width = formatters[argIndex++].toInt(); - else - TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); - if(width < 0) - { - // negative widths correspond to '-' flag set - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - width = -width; + widthSet = parseWidthOrPrecision(width, c, positionalMode, + args, argIndex, numArgs); + if (widthSet) { + if (width < 0) { + // negative widths correspond to '-' flag set + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + width = -width; + } + out.width(width); } - out.width(width); - ++c; } // 3) Parse precision - if(*c == '.') - { + if (*c == '.') { ++c; int precision = 0; - if(*c == '*') - { - ++c; - if(argIndex < numFormatters) - precision = formatters[argIndex++].toInt(); - else - TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision"); - } - else - { - if(*c >= '0' && *c <= '9') - precision = parseIntAndAdvance(c); - else if(*c == '-') // negative precisions ignored, treated as zero. - parseIntAndAdvance(++c); - } - out.precision(precision); - precisionSet = true; + parseWidthOrPrecision(precision, c, positionalMode, + args, argIndex, numArgs); + // Presence of `.` indicates precision set, unless the inferred value + // was negative in which case the default is used. + precisionSet = precision >= 0; + if (precisionSet) + out.precision(precision); } // 4) Ignore any C99 length modifier - while(*c == 'l' || *c == 'h' || *c == 'L' || - *c == 'j' || *c == 'z' || *c == 't') + while (*c == 'l' || *c == 'h' || *c == 'L' || + *c == 'j' || *c == 'z' || *c == 't') { ++c; + } // 5) We're up to the conversion specifier character. // Set stream flags based on conversion specifier (thanks to the // boost::format class for forging the way here). bool intConversion = false; - switch(*c) - { + switch (*c) { case 'u': case 'd': case 'i': out.setf(std::ios::dec, std::ios::basefield); intConversion = true; @@ -738,6 +815,18 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; + case 'A': + out.setf(std::ios::uppercase); + // Falls through + case 'a': +# ifdef _MSC_VER + // Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html + // by always setting maximum precision on MSVC to avoid precision + // loss for doubles. + out.precision(13); +# endif + out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield); + break; case 'G': out.setf(std::ios::uppercase); // Falls through @@ -746,17 +835,13 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi // As in boost::format, let stream decide float format. out.flags(out.flags() & ~std::ios::floatfield); break; - case 'a': case 'A': - TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " - "are not supported"); - break; case 'c': // Handled as special case inside formatValue() break; case 's': - if(precisionSet) + if (precisionSet) ntrunc = static_cast<int>(out.precision()); - // Make %s print booleans as "true" and "false" + // Make %s print Booleans as "true" and "false" out.setf(std::ios::boolalpha); break; case 'n': @@ -770,8 +855,7 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi default: break; } - if(intConversion && precisionSet && !widthSet) - { + if (intConversion && precisionSet && !widthSet) { // "precision" for integers gives the minimum number of digits (to be // padded with zeros on the left). This isn't really supported by the // iostreams, but we can approximately simulate it with the width if @@ -786,8 +870,8 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi //------------------------------------------------------------------------------ inline void formatImpl(std::ostream& out, const char* fmt, - const detail::FormatArg* formatters, - int numFormatters) + const detail::FormatArg* args, + int numArgs) { // Saved stream state std::streamsize origWidth = out.width(); @@ -795,26 +879,34 @@ inline void formatImpl(std::ostream& out, const char* fmt, std::ios::fmtflags origFlags = out.flags(); char origFill = out.fill(); - for (int argIndex = 0; argIndex < numFormatters; ++argIndex) - { - // Parse the format string + // "Positional mode" means all format specs should be of the form "%n$..." + // with `n` an integer. We detect this in `streamStateFromFormat`. + bool positionalMode = false; + int argIndex = 0; + while (true) { fmt = printFormatStringLiteral(out, fmt); + if (*fmt == '\0') { + if (!positionalMode && argIndex < numArgs) { + TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); + } + break; + } bool spacePadPositive = false; int ntrunc = -1; - const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, - formatters, argIndex, numFormatters); - if (argIndex >= numFormatters) - { - // Check args remain after reading any variable width/precision - TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); + const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt, + args, argIndex, numArgs); + // NB: argIndex may be incremented by reading variable width/precision + // in `streamStateFromFormat`, so do the bounds check here. + if (argIndex >= numArgs) { + TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); return; } - const FormatArg& arg = formatters[argIndex]; + const FormatArg& arg = args[argIndex]; // Format the arg into the stream. - if(!spacePadPositive) + if (!spacePadPositive) { arg.format(out, fmt, fmtEnd, ntrunc); - else - { + } + else { // The following is a special case with no direct correspondence // between stream formatting and the printf() behaviour. Simulate // it crudely by formatting into a temporary string stream and @@ -824,18 +916,17 @@ inline void formatImpl(std::ostream& out, const char* fmt, tmpStream.setf(std::ios::showpos); arg.format(tmpStream, fmt, fmtEnd, ntrunc); std::string result = tmpStream.str(); // allocates... yuck. - for(size_t i = 0, iend = result.size(); i < iend; ++i) - if(result[i] == '+') result[i] = ' '; + for (size_t i = 0, iend = result.size(); i < iend; ++i) { + if (result[i] == '+') + result[i] = ' '; + } out << result; } + if (!positionalMode) + ++argIndex; fmt = fmtEnd; } - // Print remaining part of format string. - fmt = printFormatStringLiteral(out, fmt); - if(*fmt != '\0') - TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); - // Restore stream state out.width(origWidth); out.precision(origPrecision); @@ -855,14 +946,14 @@ inline void formatImpl(std::ostream& out, const char* fmt, class FormatList { public: - FormatList(detail::FormatArg* formatters, int N) - : m_formatters(formatters), m_N(N) { } + FormatList(detail::FormatArg* args, int N) + : m_args(args), m_N(N) { } friend void vformat(std::ostream& out, const char* fmt, const FormatList& list); private: - const detail::FormatArg* m_formatters; + const detail::FormatArg* m_args; int m_N; }; @@ -879,29 +970,33 @@ class FormatListN : public FormatList public: #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template<typename... Args> - explicit FormatListN(const Args&... args) + FormatListN(const Args&... args) : FormatList(&m_formatterStore[0], N), m_formatterStore { FormatArg(args)... } { static_assert(sizeof...(args) == N, "Number of args must be N"); } #else // C++98 version void init(int) {} -# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ - \ - template<TINYFORMAT_ARGTYPES(n)> \ - explicit FormatListN(TINYFORMAT_VARARGS(n)) \ - : FormatList(&m_formatterStore[0], n) \ - { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ - \ - template<TINYFORMAT_ARGTYPES(n)> \ - void init(int i, TINYFORMAT_VARARGS(n)) \ - { \ - m_formatterStore[i] = FormatArg(v1); \ - init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ +# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ + \ + template<TINYFORMAT_ARGTYPES(n)> \ + FormatListN(TINYFORMAT_VARARGS(n)) \ + : FormatList(&m_formatterStore[0], n) \ + { TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ + \ + template<TINYFORMAT_ARGTYPES(n)> \ + void init(int i, TINYFORMAT_VARARGS(n)) \ + { \ + m_formatterStore[i] = FormatArg(v1); \ + init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) # undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR #endif + FormatListN(const FormatListN& other) + : FormatList(&m_formatterStore[0], N) + { std::copy(&other.m_formatterStore[0], &other.m_formatterStore[N], + &m_formatterStore[0]); } private: FormatArg m_formatterStore[N]; @@ -956,7 +1051,7 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) /// list of format arguments is held in a single function argument. inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) { - detail::formatImpl(out, fmt, list.m_formatters, list.m_N); + detail::formatImpl(out, fmt, list.m_args, list.m_N); } @@ -993,6 +1088,7 @@ void printfln(const char* fmt, const Args&... args) std::cout << '\n'; } + #else // C++98 version inline void format(std::ostream& out, const char* fmt) @@ -1063,6 +1159,7 @@ std::string format(const std::string &fmt, const Args&... args) } // namespace tinyformat +// Added for Bitcoin Core: /** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */ #define strprintf tfm::format diff --git a/src/validation.h b/src/validation.h index d87113c41f..54f97e7213 100644 --- a/src/validation.h +++ b/src/validation.h @@ -23,7 +23,6 @@ #include <versionbits.h> #include <serialize.h> -#include <algorithm> #include <atomic> #include <map> #include <memory> diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 8f0b495ac4..36588eb7d1 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -108,12 +108,11 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt return feebumper::Result::OK; } -static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, CCoinControl& coin_control, CAmount& old_fee) +static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, CCoinControl& coin_control) { // Get the fee rate of the original transaction. This is calculated from // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the // result. - old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); CFeeRate feerate(old_fee, txSize); feerate += CFeeRate(1); @@ -309,6 +308,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo } } + old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + if (coin_control.m_feerate) { // The user provided a feeRate argument. // We calculate this here to avoid compiler warning on the cs_wallet lock @@ -319,7 +320,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo } } else { // The user did not provide a feeRate argument - new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, new_coin_control, old_fee); + new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, old_fee, new_coin_control); } // Fill in required inputs we are double-spending(all of them) diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 3eaaf3786c..2b9e8fbf2a 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -18,7 +18,7 @@ bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestinat // Generate a new key that is added to wallet CPubKey new_key; - if (!GetKeyFromPool(new_key)) { + if (!GetKeyFromPool(new_key, type)) { error = "Error: Keypool ran out, please call keypoolrefill first"; return false; } @@ -262,24 +262,19 @@ bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial& vMasterKeyIn) return true; } -bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) +bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { + if (!CanGetAddresses(internal)) { + return false; + } + if (!ReserveKeyFromKeyPool(index, keypool, internal)) { return false; } + address = GetDestinationForKey(keypool.vchPubKey, type); return true; } -void LegacyScriptPubKeyMan::KeepDestination(int64_t index) -{ - KeepKey(index); -} - -void LegacyScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) -{ - ReturnKey(index, internal, pubkey); -} - void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) { AssertLockHeld(cs_wallet); @@ -460,7 +455,7 @@ size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() unsigned int LegacyScriptPubKeyMan::GetKeyPoolSize() const { AssertLockHeld(cs_wallet); - return setInternalKeyPool.size() + setExternalKeyPool.size(); + return setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(); } int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const @@ -1092,15 +1087,20 @@ void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const m_pool_key_to_index[pubkey.GetID()] = index; } -void LegacyScriptPubKeyMan::KeepKey(int64_t nIndex) +void LegacyScriptPubKeyMan::KeepDestination(int64_t nIndex, const OutputType& type) { // Remove from key pool WalletBatch batch(m_storage.GetDatabase()); batch.ErasePool(nIndex); + CPubKey pubkey; + bool have_pk = GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey); + assert(have_pk); + LearnRelatedScripts(pubkey, type); + m_index_to_reserved_key.erase(nIndex); WalletLogPrintf("keypool keep %d\n", nIndex); } -void LegacyScriptPubKeyMan::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) +void LegacyScriptPubKeyMan::ReturnDestination(int64_t nIndex, bool fInternal, const CTxDestination&) { // Return to key pool { @@ -1112,13 +1112,15 @@ void LegacyScriptPubKeyMan::ReturnKey(int64_t nIndex, bool fInternal, const CPub } else { setExternalKeyPool.insert(nIndex); } - m_pool_key_to_index[pubkey.GetID()] = nIndex; + CKeyID& pubkey_id = m_index_to_reserved_key.at(nIndex); + m_pool_key_to_index[pubkey_id] = nIndex; + m_index_to_reserved_key.erase(nIndex); NotifyCanGetAddressesChanged(); } WalletLogPrintf("keypool return %d\n", nIndex); } -bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, bool internal) +bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType type, bool internal) { if (!CanGetAddresses(internal)) { return false; @@ -1134,7 +1136,7 @@ bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, bool internal) result = GenerateNewKey(batch, internal); return true; } - KeepKey(nIndex); + KeepDestination(nIndex, type); result = keypool.vchPubKey; } return true; @@ -1179,6 +1181,8 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key throw std::runtime_error(std::string(__func__) + ": keypool entry invalid"); } + assert(m_index_to_reserved_key.count(nIndex) == 0); + m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID(); m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); WalletLogPrintf("keypool reserve %d\n", nIndex); } diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 4f17156792..6ed9a4787a 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -150,9 +150,9 @@ public: virtual bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { return false; } virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; } - virtual bool GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) { return false; } - virtual void KeepDestination(int64_t index) {} - virtual void ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) {} + virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { return false; } + virtual void KeepDestination(int64_t index, const OutputType& type) {} + virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {} virtual bool TopUp(unsigned int size = 0) { return false; } @@ -246,9 +246,11 @@ private: std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_wallet); int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; std::map<CKeyID, int64_t> m_pool_key_to_index; + // Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it + std::map<int64_t, CKeyID> m_index_to_reserved_key; //! Fetches a key from the keypool - bool GetKeyFromPool(CPubKey &key, bool internal = false); + bool GetKeyFromPool(CPubKey &key, const OutputType type, bool internal = false); /** * Reserves a key from the keypool and sets nIndex to its index @@ -266,9 +268,6 @@ private: */ bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); - void KeepKey(int64_t nIndex); - void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); - public: bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) override; isminetype IsMine(const CScript& script) const override; @@ -276,9 +275,9 @@ public: //! will encrypt previously unencrypted keys bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); - bool GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override; - void KeepDestination(int64_t index) override; - void ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) override; + bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override; + void KeepDestination(int64_t index, const OutputType& type) override; + void ReturnDestination(int64_t index, bool internal, const CTxDestination&) override; bool TopUp(unsigned int size = 0) override; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 647b381b39..abee497c1d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -822,7 +822,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) void CWallet::LoadToWallet(CWalletTx& wtxIn) { - // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken. + // If wallet doesn't have a chain (e.g bitcoin-wallet), lock can't be taken. auto locked_chain = LockChain(); if (locked_chain) { Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock); @@ -2944,7 +2944,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { // Even if we don't use this lock in this function, we want to preserve // lock order in LoadToWallet if query of chain state is needed to know - // tx status. If lock can't be taken (e.g wallet-tool), tx confirmation + // tx status. If lock can't be taken (e.g bitcoin-wallet), tx confirmation // status may be not reliable. auto locked_chain = LockChain(); LOCK(cs_wallet); @@ -3295,21 +3295,15 @@ bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool inter return false; } - if (!pwallet->CanGetAddresses(internal)) { - return false; - } if (nIndex == -1) { CKeyPool keypool; - if (!m_spk_man->GetReservedDestination(type, internal, nIndex, keypool)) { + if (!m_spk_man->GetReservedDestination(type, internal, address, nIndex, keypool)) { return false; } - vchPubKey = keypool.vchPubKey; fInternal = keypool.fInternal; } - assert(vchPubKey.IsValid()); - address = GetDestinationForKey(vchPubKey, type); dest = address; return true; } @@ -3317,21 +3311,18 @@ bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool inter void ReserveDestination::KeepDestination() { if (nIndex != -1) { - m_spk_man->KeepDestination(nIndex); - m_spk_man->LearnRelatedScripts(vchPubKey, type); + m_spk_man->KeepDestination(nIndex, type); } nIndex = -1; - vchPubKey = CPubKey(); address = CNoDestination(); } void ReserveDestination::ReturnDestination() { if (nIndex != -1) { - m_spk_man->ReturnDestination(nIndex, fInternal, vchPubKey); + m_spk_man->ReturnDestination(nIndex, fInternal, address); } nIndex = -1; - vchPubKey = CPubKey(); address = CNoDestination(); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 51df4a9a8b..b02e092f0a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -139,12 +139,11 @@ class ReserveDestination protected: //! The wallet to reserve from CWallet* const pwallet; - LegacyScriptPubKeyMan* m_spk_man{nullptr}; + //! The ScriptPubKeyMan to reserve from. Based on type when GetReservedDestination is called + ScriptPubKeyMan* m_spk_man{nullptr}; OutputType const type; //! The index of the address's key in the keypool int64_t nIndex{-1}; - //! The public key for the address - CPubKey vchPubKey; //! The destination CTxDestination address; //! Whether this is from the internal (change output) keypool |