diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.test.include | 14 | ||||
-rw-r--r-- | src/addrman.h | 78 | ||||
-rw-r--r-- | src/chainparams.cpp | 4 | ||||
-rw-r--r-- | src/dummywallet.cpp | 1 | ||||
-rw-r--r-- | src/init.cpp | 2 | ||||
-rw-r--r-- | src/netaddress.cpp | 6 | ||||
-rw-r--r-- | src/netaddress.h | 2 | ||||
-rw-r--r-- | src/qt/createwalletdialog.cpp | 27 | ||||
-rw-r--r-- | src/qt/forms/createwalletdialog.ui | 32 | ||||
-rw-r--r-- | src/qt/transactiontablemodel.cpp | 75 | ||||
-rw-r--r-- | src/test/fuzz/addrman.cpp | 119 | ||||
-rw-r--r-- | src/test/fuzz/connman.cpp | 162 | ||||
-rw-r--r-- | src/test/fuzz/merkleblock.cpp | 34 | ||||
-rw-r--r-- | src/test/fuzz/net.cpp | 2 | ||||
-rw-r--r-- | src/test/fuzz/util.h | 41 | ||||
-rw-r--r-- | src/test/util/setup_common.h | 9 | ||||
-rw-r--r-- | src/validation.cpp | 2 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 7 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.cpp | 13 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.h | 6 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 4 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 56 | ||||
-rw-r--r-- | src/wallet/wallet.h | 18 | ||||
-rw-r--r-- | src/wallet/walletutil.cpp | 57 | ||||
-rw-r--r-- | src/wallet/walletutil.h | 3 |
25 files changed, 607 insertions, 167 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 7fac78f973..9cc383c240 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -7,6 +7,7 @@ FUZZ_TARGETS = \ test/fuzz/addr_info_deserialize \ test/fuzz/addrdb \ test/fuzz/address_deserialize \ + test/fuzz/addrman \ test/fuzz/addrman_deserialize \ test/fuzz/asmap \ test/fuzz/asmap_direct \ @@ -35,6 +36,7 @@ FUZZ_TARGETS = \ test/fuzz/checkqueue \ test/fuzz/coins_deserialize \ test/fuzz/coins_view \ + test/fuzz/connman \ test/fuzz/crypto \ test/fuzz/crypto_aes256 \ test/fuzz/crypto_aes256cbc \ @@ -352,6 +354,12 @@ test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_address_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) test_fuzz_address_deserialize_SOURCES = test/fuzz/deserialize.cpp +test_fuzz_addrman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_addrman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_addrman_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_addrman_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) +test_fuzz_addrman_SOURCES = test/fuzz/addrman.cpp + test_fuzz_addrman_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRMAN_DESERIALIZE=1 test_fuzz_addrman_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_addrman_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) @@ -520,6 +528,12 @@ test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_coins_view_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp +test_fuzz_connman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_connman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_connman_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_connman_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) +test_fuzz_connman_SOURCES = test/fuzz/connman.cpp + test_fuzz_crypto_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_fuzz_crypto_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_crypto_LDADD = $(FUZZ_SUITE_LD_COMMON) diff --git a/src/addrman.h b/src/addrman.h index b4089dc894..04dd30b375 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -7,6 +7,7 @@ #define BITCOIN_ADDRMAN_H #include <clientversion.h> +#include <config/bitcoin-config.h> #include <netaddress.h> #include <protocol.h> #include <random.h> @@ -176,6 +177,28 @@ protected: mutable RecursiveMutex cs; private: + //! Serialization versions. + enum Format : uint8_t { + V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88 + V1_DETERMINISTIC = 1, //!< for pre-asmap files + V2_ASMAP = 2, //!< for files including asmap version + V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format + }; + + //! The maximum format this software knows it can unserialize. Also, we always serialize + //! in this format. + //! The format (first byte in the serialized stream) can be higher than this and + //! still this software may be able to unserialize the file - if the second byte + //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this. + static constexpr Format FILE_FORMAT = Format::V3_BIP155; + + //! The initial value of a field that is incremented every time an incompatible format + //! change is made (such that old software versions would not be able to parse and + //! understand the new file format). This is 32 because we overtook the "key size" + //! field which was 32 historically. + //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead. + static constexpr uint8_t INCOMPATIBILITY_BASE = 32; + //! last used nId int nIdCount GUARDED_BY(cs); @@ -265,14 +288,6 @@ protected: void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs); public: - //! Serialization versions. - enum class Format : uint8_t { - V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88 - V1_DETERMINISTIC = 1, //!< for pre-asmap files - V2_ASMAP = 2, //!< for files including asmap version - V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format - }; - // Compressed IP->ASN mapping, loaded from a file when a node starts. // Should be always empty if no file was provided. // This mapping is then used for bucketing nodes in Addrman. @@ -295,8 +310,18 @@ public: /** * Serialized format. - * * version byte (@see `Format`) - * * 0x20 + nKey (serialized as if it were a vector, for backward compatibility) + * * format version byte (@see `Format`) + * * lowest compatible format version byte. This is used to help old software decide + * whether to parse the file. For example: + * * Bitcoin Core version N knows how to parse up to format=3. If a new format=4 is + * introduced in version N+1 that is compatible with format=3 and it is known that + * version N will be able to parse it, then version N+1 will write + * (format=4, lowest_compatible=3) in the first two bytes of the file, and so + * version N will still try to parse it. + * * Bitcoin Core version N+2 introduces a new incompatible format=5. It will write + * (format=5, lowest_compatible=5) and so any versions that do not know how to parse + * format=5 will not try to read the file. + * * nKey * * nNew * * nTried * * number of "new" buckets XOR 2**30 @@ -327,12 +352,17 @@ public: { LOCK(cs); - // Always serialize in the latest version (currently Format::V3_BIP155). + // Always serialize in the latest version (FILE_FORMAT). OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT); - s << static_cast<uint8_t>(Format::V3_BIP155); - s << ((unsigned char)32); + s << static_cast<uint8_t>(FILE_FORMAT); + + // Increment `lowest_compatible` iff a newly introduced format is incompatible with + // the previous one. + static constexpr uint8_t lowest_compatible = Format::V3_BIP155; + s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible); + s << nKey; s << nNew; s << nTried; @@ -392,15 +422,6 @@ public: Format format; s_ >> Using<CustomUintFormatter<1>>(format); - static constexpr Format maximum_supported_format = Format::V3_BIP155; - if (format > maximum_supported_format) { - throw std::ios_base::failure(strprintf( - "Unsupported format of addrman database: %u. Maximum supported is %u. " - "Continuing operation without using the saved list of peers.", - static_cast<uint8_t>(format), - static_cast<uint8_t>(maximum_supported_format))); - } - int stream_version = s_.GetVersion(); if (format >= Format::V3_BIP155) { // Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress @@ -410,9 +431,16 @@ public: OverrideStream<Stream> s(&s_, s_.GetType(), stream_version); - unsigned char nKeySize; - s >> nKeySize; - if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization"); + uint8_t compat; + s >> compat; + const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE; + if (lowest_compatible > FILE_FORMAT) { + throw std::ios_base::failure(strprintf( + "Unsupported format of addrman database: %u. It is compatible with formats >=%u, " + "but the maximum supported by this version of %s is %u.", + format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT))); + } + s >> nKey; s >> nNew; s >> nTried; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9c32f0db4c..fedb032db2 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -322,8 +322,8 @@ public: consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowNoRetargeting = false; - consensus.nRuleChangeActivationThreshold = 1916; - consensus.nMinerConfirmationWindow = 2016; + consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing consensus.MinBIP9WarningHeight = 0; consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000"); consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp index 8d2dcd0279..4543f098a1 100644 --- a/src/dummywallet.cpp +++ b/src/dummywallet.cpp @@ -40,7 +40,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>", - "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", diff --git a/src/init.cpp b/src/init.cpp index 1387d6b982..495d96f938 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1145,7 +1145,7 @@ bool AppInitParameterInteraction(const ArgsManager& args) if (!ParseMoney(args.GetArg("-minrelaytxfee", ""), n)) { return InitError(AmountErrMsg("minrelaytxfee", args.GetArg("-minrelaytxfee", ""))); } - // High fee check is done afterward in CWallet::CreateWalletFromFile() + // High fee check is done afterward in CWallet::Create() ::minRelayTxFee = CFeeRate(n); } else if (incrementalRelayFee > ::minRelayTxFee) { // Allow only setting incrementalRelayFee to control both diff --git a/src/netaddress.cpp b/src/netaddress.cpp index c0193fa2e9..35e9161f58 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -255,10 +255,14 @@ bool CNetAddr::SetSpecial(const std::string& str) Span<const uint8_t> input_checksum{input.data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN}; Span<const uint8_t> input_version{input.data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)}; + if (input_version != torv3::VERSION) { + return false; + } + uint8_t calculated_checksum[torv3::CHECKSUM_LEN]; torv3::Checksum(input_pubkey, calculated_checksum); - if (input_checksum != calculated_checksum || input_version != torv3::VERSION) { + if (input_checksum != calculated_checksum) { return false; } diff --git a/src/netaddress.h b/src/netaddress.h index f35b01d202..29b2eaafeb 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -29,7 +29,7 @@ * Make sure that this does not collide with any of the values in `version.h` * or with `SERIALIZE_TRANSACTION_NO_WITNESS`. */ -static const int ADDRV2_FORMAT = 0x20000000; +static constexpr int ADDRV2_FORMAT = 0x20000000; /** * A network type. diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp index 38c6bfe56a..2ded6a1d89 100644 --- a/src/qt/createwalletdialog.cpp +++ b/src/qt/createwalletdialog.cpp @@ -35,11 +35,28 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) : } }); -#ifndef USE_SQLITE - ui->descriptor_checkbox->setToolTip(tr("Compiled without sqlite support (required for descriptor wallets)")); - ui->descriptor_checkbox->setEnabled(false); - ui->descriptor_checkbox->setChecked(false); -#endif + connect(ui->disable_privkeys_checkbox, &QCheckBox::toggled, [this](bool checked) { + // Disable the encrypt_wallet_checkbox when isDisablePrivateKeysChecked is + // set to true, enable it when isDisablePrivateKeysChecked is false. + ui->encrypt_wallet_checkbox->setEnabled(!checked); + + // Wallets without private keys start out blank + if (checked) { + ui->blank_wallet_checkbox->setChecked(true); + } + + // When the encrypt_wallet_checkbox is disabled, uncheck it. + if (!ui->encrypt_wallet_checkbox->isEnabled()) { + ui->encrypt_wallet_checkbox->setChecked(false); + } + }); + + #ifndef USE_SQLITE + ui->descriptor_checkbox->setToolTip(tr("Compiled without sqlite support (required for descriptor wallets)")); + ui->descriptor_checkbox->setEnabled(false); + ui->descriptor_checkbox->setChecked(false); + #endif + } CreateWalletDialog::~CreateWalletDialog() diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui index b592140dd7..ea713e1abd 100644 --- a/src/qt/forms/createwalletdialog.ui +++ b/src/qt/forms/createwalletdialog.ui @@ -38,6 +38,9 @@ <height>24</height> </rect> </property> + <property name="placeholderText"> + <string>Wallet</string> + </property> </widget> <widget class="QLabel" name="label"> <property name="geometry"> @@ -68,17 +71,33 @@ <string>Encrypt Wallet</string> </property> <property name="checked"> - <bool>true</bool> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" name="advanced_options_label"> + <property name="geometry"> + <rect> + <x>20</x> + <y>90</y> + <width>130</width> + <height>21</height> + </rect> + </property> + <property name="styleSheet"> + <string notr="true">font-weight:bold;</string> + </property> + <property name="text"> + <string>Advanced options</string> </property> </widget> <widget class="QCheckBox" name="disable_privkeys_checkbox"> <property name="enabled"> - <bool>false</bool> + <bool>true</bool> </property> <property name="geometry"> <rect> <x>20</x> - <y>80</y> + <y>115</y> <width>171</width> <height>22</height> </rect> @@ -94,8 +113,8 @@ <property name="geometry"> <rect> <x>20</x> - <y>110</y> - <width>171</width> + <y>135</y> + <width>220</width> <height>22</height> </rect> </property> @@ -110,7 +129,7 @@ <property name="geometry"> <rect> <x>20</x> - <y>140</y> + <y>155</y> <width>171</width> <height>22</height> </rect> @@ -128,6 +147,7 @@ <tabstop>encrypt_wallet_checkbox</tabstop> <tabstop>disable_privkeys_checkbox</tabstop> <tabstop>blank_wallet_checkbox</tabstop> + <tabstop>descriptor_checkbox</tabstop> </tabstops> <resources/> <connections> diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index c560dc58e7..3148089b52 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -54,6 +54,30 @@ struct TxLessThan } }; +// queue notifications to show a non freezing progress dialog e.g. for rescan +struct TransactionNotification +{ +public: + TransactionNotification() {} + TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction): + hash(_hash), status(_status), showTransaction(_showTransaction) {} + + void invoke(QObject *ttm) + { + QString strHash = QString::fromStdString(hash.GetHex()); + qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status); + bool invoked = QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection, + Q_ARG(QString, strHash), + Q_ARG(int, status), + Q_ARG(bool, showTransaction)); + assert(invoked); + } +private: + uint256 hash; + ChangeType status; + bool showTransaction; +}; + // Private implementation class TransactionTablePriv { @@ -71,6 +95,12 @@ public: */ QList<TransactionRecord> cachedWallet; + bool fQueueNotifications = false; + std::vector< TransactionNotification > vQueueNotifications; + + void NotifyTransactionChanged(const uint256 &hash, ChangeType status); + void ShowProgress(const std::string &title, int nProgress); + /* Query entire wallet anew from core. */ void refreshWallet(interfaces::Wallet& wallet) @@ -674,34 +704,7 @@ void TransactionTableModel::updateDisplayUnit() Q_EMIT dataChanged(index(0, Amount), index(priv->size()-1, Amount)); } -// queue notifications to show a non freezing progress dialog e.g. for rescan -struct TransactionNotification -{ -public: - TransactionNotification() {} - TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction): - hash(_hash), status(_status), showTransaction(_showTransaction) {} - - void invoke(QObject *ttm) - { - QString strHash = QString::fromStdString(hash.GetHex()); - qDebug() << "NotifyTransactionChanged: " + strHash + " status= " + QString::number(status); - bool invoked = QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection, - Q_ARG(QString, strHash), - Q_ARG(int, status), - Q_ARG(bool, showTransaction)); - assert(invoked); - } -private: - uint256 hash; - ChangeType status; - bool showTransaction; -}; - -static bool fQueueNotifications = false; -static std::vector< TransactionNotification > vQueueNotifications; - -static void NotifyTransactionChanged(TransactionTableModel *ttm, const uint256 &hash, ChangeType status) +void TransactionTablePriv::NotifyTransactionChanged(const uint256 &hash, ChangeType status) { // Find transaction in wallet // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread) @@ -714,10 +717,10 @@ static void NotifyTransactionChanged(TransactionTableModel *ttm, const uint256 & vQueueNotifications.push_back(notification); return; } - notification.invoke(ttm); + notification.invoke(parent); } -static void ShowProgress(TransactionTableModel *ttm, const std::string &title, int nProgress) +void TransactionTablePriv::ShowProgress(const std::string &title, int nProgress) { if (nProgress == 0) fQueueNotifications = true; @@ -726,27 +729,27 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i { fQueueNotifications = false; if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons - bool invoked = QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); + bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); assert(invoked); } for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) { if (vQueueNotifications.size() - i <= 10) { - bool invoked = QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); + bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); assert(invoked); } - vQueueNotifications[i].invoke(ttm); + vQueueNotifications[i].invoke(parent); } - std::vector<TransactionNotification >().swap(vQueueNotifications); // clear + vQueueNotifications.clear(); } } void TransactionTableModel::subscribeToCoreSignals() { // Connect signals to wallet - m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2)); - m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)); + m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(&TransactionTablePriv::NotifyTransactionChanged, priv, std::placeholders::_1, std::placeholders::_2)); + m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(&TransactionTablePriv::ShowProgress, priv, std::placeholders::_1, std::placeholders::_2)); } void TransactionTableModel::unsubscribeFromCoreSignals() diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp new file mode 100644 index 0000000000..0ceeea2d36 --- /dev/null +++ b/src/test/fuzz/addrman.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <addrdb.h> +#include <addrman.h> +#include <chainparams.h> +#include <merkleblock.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <time.h> +#include <util/asmap.h> + +#include <cstdint> +#include <optional> +#include <string> +#include <vector> + +void initialize() +{ + SelectParams(CBaseChainParams::REGTEST); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + + SetMockTime(ConsumeTime(fuzzed_data_provider)); + CAddrMan addr_man; + if (fuzzed_data_provider.ConsumeBool()) { + addr_man.m_asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); + if (!SanityCheckASMap(addr_man.m_asmap)) { + addr_man.m_asmap.clear(); + } + } + while (fuzzed_data_provider.ConsumeBool()) { + switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) { + case 0: { + addr_man.Clear(); + break; + } + case 1: { + addr_man.ResolveCollisions(); + break; + } + case 2: { + (void)addr_man.SelectTriedCollision(); + break; + } + case 3: { + (void)addr_man.Select(fuzzed_data_provider.ConsumeBool()); + break; + } + case 4: { + (void)addr_man.GetAddr(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); + break; + } + case 5: { + const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); + const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); + if (opt_address && opt_net_addr) { + addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000)); + } + break; + } + case 6: { + std::vector<CAddress> addresses; + while (fuzzed_data_provider.ConsumeBool()) { + const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); + if (!opt_address) { + break; + } + addresses.push_back(*opt_address); + } + const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); + if (opt_net_addr) { + addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000)); + } + break; + } + case 7: { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + } + break; + } + case 8: { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + } + break; + } + case 9: { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider)); + } + break; + } + case 10: { + const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); + if (opt_service) { + addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()}); + } + break; + } + case 11: { + (void)addr_man.Check(); + break; + } + } + } + (void)addr_man.size(); + CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION); + data_stream << addr_man; +} diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp new file mode 100644 index 0000000000..6521c3f3b2 --- /dev/null +++ b/src/test/fuzz/connman.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <chainparams.h> +#include <chainparamsbase.h> +#include <net.h> +#include <netaddress.h> +#include <protocol.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <util/translation.h> + +#include <cstdint> +#include <vector> + +void initialize() +{ + InitializeFuzzingContext(); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeBool()}; + CAddress random_address; + CNetAddr random_netaddr; + CNode random_node = ConsumeNode(fuzzed_data_provider); + CService random_service; + CSubNet random_subnet; + std::string random_string; + while (fuzzed_data_provider.ConsumeBool()) { + switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 30)) { + case 0: + random_address = ConsumeAddress(fuzzed_data_provider); + break; + case 1: + random_netaddr = ConsumeNetAddr(fuzzed_data_provider); + break; + case 2: + random_service = ConsumeService(fuzzed_data_provider); + break; + case 3: + random_subnet = ConsumeSubNet(fuzzed_data_provider); + break; + case 4: + random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); + break; + case 5: { + std::vector<CAddress> addresses; + while (fuzzed_data_provider.ConsumeBool()) { + addresses.push_back(ConsumeAddress(fuzzed_data_provider)); + } + // Limit nTimePenalty to int32_t to avoid signed integer overflow + (void)connman.AddNewAddresses(addresses, ConsumeAddress(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>()); + break; + } + case 6: + connman.AddNode(random_string); + break; + case 7: + connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + break; + case 8: + connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral<NodeId>()); + break; + case 9: + connman.DisconnectNode(random_netaddr); + break; + case 10: + connman.DisconnectNode(random_string); + break; + case 11: + connman.DisconnectNode(random_subnet); + break; + case 12: + connman.ForEachNode([](auto) {}); + break; + case 13: + connman.ForEachNodeThen([](auto) {}, []() {}); + break; + case 14: + (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); + break; + case 15: + (void)connman.GetAddresses(fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); + break; + case 16: { + (void)connman.GetAddresses(random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); + break; + } + case 17: + (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + break; + case 18: + (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); + break; + case 19: + connman.MarkAddressGood(random_address); + break; + case 20: + (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool()); + break; + case 21: + // Limit now to int32_t to avoid signed integer overflow + (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>()); + break; + case 22: { + CSerializedNetMsg serialized_net_msg; + serialized_net_msg.m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE); + serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + connman.PushMessage(&random_node, std::move(serialized_net_msg)); + break; + } + case 23: + connman.RemoveAddedNode(random_string); + break; + case 24: { + const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); + if (SanityCheckASMap(asmap)) { + connman.SetAsmap(asmap); + } + break; + } + case 25: + connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral<int>()); + break; + case 26: + connman.SetMaxOutboundTarget(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + break; + case 27: + connman.SetMaxOutboundTimeframe(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + break; + case 28: + connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool()); + break; + case 29: + connman.SetServices(random_service, static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>())); + break; + case 30: + connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool()); + break; + } + } + (void)connman.GetAddedNodeInfo(); + (void)connman.GetBestHeight(); + (void)connman.GetExtraOutboundCount(); + (void)connman.GetLocalServices(); + (void)connman.GetMaxOutboundTarget(); + (void)connman.GetMaxOutboundTimeframe(); + (void)connman.GetMaxOutboundTimeLeftInCycle(); + (void)connman.GetNetworkActive(); + std::vector<CNodeStats> stats; + connman.GetNodeStats(stats); + (void)connman.GetOutboundTargetBytesLeft(); + (void)connman.GetReceiveFloodSize(); + (void)connman.GetTotalBytesRecv(); + (void)connman.GetTotalBytesSent(); + (void)connman.GetTryNewOutboundPeer(); + (void)connman.GetUseAddrmanOutgoing(); +} diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp index c44e334272..4710e75757 100644 --- a/src/test/fuzz/merkleblock.cpp +++ b/src/test/fuzz/merkleblock.cpp @@ -16,12 +16,36 @@ void test_one_input(const std::vector<uint8_t>& buffer) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - std::optional<CPartialMerkleTree> partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider); - if (!partial_merkle_tree) { - return; + CPartialMerkleTree partial_merkle_tree; + switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1)) { + case 0: { + const std::optional<CPartialMerkleTree> opt_partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider); + if (opt_partial_merkle_tree) { + partial_merkle_tree = *opt_partial_merkle_tree; + } + break; } - (void)partial_merkle_tree->GetNumTransactions(); + case 1: { + CMerkleBlock merkle_block; + const std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider); + CBloomFilter bloom_filter; + std::set<uint256> txids; + if (opt_block && !opt_block->vtx.empty()) { + if (fuzzed_data_provider.ConsumeBool()) { + merkle_block = CMerkleBlock{*opt_block, bloom_filter}; + } else if (fuzzed_data_provider.ConsumeBool()) { + while (fuzzed_data_provider.ConsumeBool()) { + txids.insert(ConsumeUInt256(fuzzed_data_provider)); + } + merkle_block = CMerkleBlock{*opt_block, txids}; + } + } + partial_merkle_tree = merkle_block.txn; + break; + } + } + (void)partial_merkle_tree.GetNumTransactions(); std::vector<uint256> matches; std::vector<unsigned int> indices; - (void)partial_merkle_tree->ExtractMatches(matches, indices); + (void)partial_merkle_tree.ExtractMatches(matches, indices); } diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index 3818838765..c61d406291 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -63,7 +63,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) break; } case 3: { - const std::vector<bool> asmap = ConsumeRandomLengthIntegralVector<bool>(fuzzed_data_provider, 128); + const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); if (!SanityCheckASMap(asmap)) { break; } diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index ed6093a8a8..e99ed8d72d 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -11,6 +11,8 @@ #include <chainparamsbase.h> #include <coins.h> #include <consensus/consensus.h> +#include <merkleblock.h> +#include <net.h> #include <netaddress.h> #include <netbase.h> #include <primitives/transaction.h> @@ -23,6 +25,7 @@ #include <test/util/setup_common.h> #include <txmempool.h> #include <uint256.h> +#include <util/time.h> #include <version.h> #include <algorithm> @@ -38,6 +41,11 @@ NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataPr return {s.begin(), s.end()}; } +NODISCARD inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept +{ + return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length)); +} + NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { return {ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION}; @@ -88,6 +96,13 @@ NODISCARD inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider) return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY); } +NODISCARD inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:00Z"); + static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z"); + return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max); +} + NODISCARD inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept { const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); @@ -260,6 +275,32 @@ CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()}; } +CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()}; +} + +CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()), fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; +} + +CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>(); + const ServiceFlags local_services = static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + const int my_starting_height = fuzzed_data_provider.ConsumeIntegral<int>(); + const SOCKET socket = INVALID_SOCKET; + const CAddress address = ConsumeAddress(fuzzed_data_provider); + const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); + const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); + const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider); + const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64); + const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}); + const bool inbound_onion = fuzzed_data_provider.ConsumeBool(); + return {node_id, local_services, my_starting_height, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion}; +} + void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST) { static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}}; diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index a09c8c122d..1812ce1666 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -11,6 +11,7 @@ #include <node/context.h> #include <pubkey.h> #include <random.h> +#include <stdexcept> #include <txmempool.h> #include <util/check.h> #include <util/string.h> @@ -158,13 +159,15 @@ std::ostream& operator<<(std::ostream& os, const uint256& num); * Use as * BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo")); */ -class HasReason { +class HasReason +{ public: explicit HasReason(const std::string& reason) : m_reason(reason) {} - template <typename E> - bool operator() (const E& e) const { + bool operator()(const std::exception& e) const + { return std::string(e.what()).find(m_reason) != std::string::npos; }; + private: const std::string m_reason; }; diff --git a/src/validation.cpp b/src/validation.cpp index 8241cb159f..feb7502a0f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5084,7 +5084,7 @@ bool LoadMempool(CTxMemPool& pool) pool.PrioritiseTransaction(tx->GetHash(), amountdelta); } TxValidationState state; - if (nTime + nExpiryTimeout > nNow) { + if (nTime > nNow - nExpiryTimeout) { LOCK(cs_main); AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime, nullptr /* plTxnReplaced */, false /* bypass_limits */, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 25c6cf306d..6efa3d0c27 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8,6 +8,7 @@ #include <interfaces/chain.h> #include <key_io.h> #include <node/context.h> +#include <optional.h> #include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> @@ -3603,7 +3604,7 @@ static RPCHelpMan rescanblockchain() } int start_height = 0; - Optional<int> stop_height; + Optional<int> stop_height = MakeOptional(false, int()); uint256 start_block; { LOCK(pwallet->cs_wallet); @@ -4500,10 +4501,8 @@ static RPCHelpMan upgradewallet() if (!request.params[0].isNull()) { version = request.params[0].get_int(); } - bilingual_str error; - std::vector<bilingual_str> warnings; - if (!pwallet->UpgradeWallet(version, error, warnings)) { + if (!pwallet->UpgradeWallet(version, error)) { throw JSONRPCError(RPC_WALLET_ERROR, error.original); } UniValue obj(UniValue::VOBJ); diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 188289b010..d2e1be6402 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -438,12 +438,12 @@ bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const return keypool_has_keys; } -bool LegacyScriptPubKeyMan::Upgrade(int prev_version, bilingual_str& error) +bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual_str& error) { LOCK(cs_KeyStore); bool hd_upgrade = false; bool split_upgrade = false; - if (m_storage.CanSupportFeature(FEATURE_HD) && !IsHDEnabled()) { + if (IsFeatureSupported(new_version, FEATURE_HD) && !IsHDEnabled()) { WalletLogPrintf("Upgrading wallet to HD\n"); m_storage.SetMinVersion(FEATURE_HD); @@ -453,10 +453,17 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, bilingual_str& error) hd_upgrade = true; } // Upgrade to HD chain split if necessary - if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { + if (IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) { WalletLogPrintf("Upgrading wallet to use HD chain split\n"); m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); split_upgrade = FEATURE_HD_SPLIT > prev_version; + // Upgrade the HDChain + if (m_hd_chain.nVersion < CHDChain::VERSION_HD_CHAIN_SPLIT) { + m_hd_chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; + if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(m_hd_chain)) { + throw std::runtime_error(std::string(__func__) + ": writing chain failed"); + } + } } // Mark all keys currently in the keypool as pre-split if (split_upgrade) { diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 63c10b7a0d..3bf8f78120 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -37,7 +37,7 @@ public: virtual bool IsWalletFlagSet(uint64_t) const = 0; virtual void UnsetBlankWalletFlag(WalletBatch&) = 0; virtual bool CanSupportFeature(enum WalletFeature) const = 0; - virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr, bool = false) = 0; + virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr) = 0; virtual const CKeyingMaterial& GetEncryptionKey() const = 0; virtual bool HasEncryptionKeys() const = 0; virtual bool IsLocked() const = 0; @@ -206,7 +206,7 @@ public: virtual bool CanGetAddresses(bool internal = false) const { return false; } /** Upgrades the wallet to the specified version */ - virtual bool Upgrade(int prev_version, bilingual_str& error) { return false; } + virtual bool Upgrade(int prev_version, int new_version, bilingual_str& error) { return false; } virtual bool HavePrivateKeys() const { return false; } @@ -371,7 +371,7 @@ public: bool SetupGeneration(bool force = false) override; - bool Upgrade(int prev_version, bilingual_str& error) override; + bool Upgrade(int prev_version, int new_version, bilingual_str& error) override; bool HavePrivateKeys() const override; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index c42114c394..4911af08c6 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -688,7 +688,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup) BOOST_CHECK_EXCEPTION(vr >> w_desc, std::ios_base::failure, malformed_descriptor); } -//! Test CreateWalletFromFile function and its behavior handling potential race +//! Test CWallet::Create() and its behavior handling potential race //! conditions if it's called the same time an incoming transaction shows up in //! the mempool or a new block. //! @@ -706,7 +706,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup) //! wallet rescan and notifications are immediately synced, to verify the wallet //! must already have a handler in place for them, and there's no gap after //! rescanning where new transactions in new blocks could be lost. -BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup) +BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup) { // Create new wallet with known key and unload it. auto chain = interfaces::MakeChain(m_node); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 37f6a4a7dc..ff8bfff872 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -436,21 +436,13 @@ void CWallet::chainStateFlushed(const CBlockLocator& loc) batch.WriteBestBlock(loc); } -void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, bool fExplicit) +void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in) { LOCK(cs_wallet); if (nWalletVersion >= nVersion) return; - - // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way - if (fExplicit && nVersion > nWalletMaxVersion) - nVersion = FEATURE_LATEST; - nWalletVersion = nVersion; - if (nVersion > nWalletMaxVersion) - nWalletMaxVersion = nVersion; - { WalletBatch* batch = batch_in ? batch_in : new WalletBatch(*database); if (nWalletVersion > 40000) @@ -460,18 +452,6 @@ void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, } } -bool CWallet::SetMaxVersion(int nVersion) -{ - LOCK(cs_wallet); - // cannot downgrade below current version - if (nWalletVersion > nVersion) - return false; - - nWalletMaxVersion = nVersion; - - return true; -} - std::set<uint256> CWallet::GetConflicts(const uint256& txid) const { std::set<uint256> result; @@ -656,7 +636,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) } // Encryption was introduced in version 0.4.0 - SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch, true); + SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch); if (!encrypted_batch->TxnCommit()) { delete encrypted_batch; @@ -1778,7 +1758,11 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc double progress_current = progress_begin; int block_height = start_height; while (!fAbortRescan && !chain().shutdownRequested()) { - m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin); + if (progress_end - progress_begin > 0.0) { + m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin); + } else { // avoid divide-by-zero for single block scan range (i.e. start and stop hashes are equal) + m_scanning_progress = 0; + } if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) { ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100)))); } @@ -3106,10 +3090,10 @@ bool CWallet::CreateTransactionInternal( WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, feeCalc.est.pass.start, feeCalc.est.pass.end, - 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), + (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0, feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, feeCalc.est.fail.start, feeCalc.est.fail.end, - 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool), + (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0, feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool); return true; } @@ -4118,36 +4102,34 @@ const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest return &address_book_it->second; } -bool CWallet::UpgradeWallet(int version, bilingual_str& error, std::vector<bilingual_str>& warnings) +bool CWallet::UpgradeWallet(int version, bilingual_str& error) { int prev_version = GetVersion(); - int nMaxVersion = version; - if (nMaxVersion == 0) // the -upgradewallet without argument case - { + if (version == 0) { WalletLogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); - nMaxVersion = FEATURE_LATEST; - SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately + version = FEATURE_LATEST; } else { - WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); + WalletLogPrintf("Allowing wallet upgrade up to %i\n", version); } - if (nMaxVersion < GetVersion()) + if (version < prev_version) { error = _("Cannot downgrade wallet"); return false; } - SetMaxVersion(nMaxVersion); LOCK(cs_wallet); // Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT - int max_version = GetVersion(); - if (!CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) { + if (!CanSupportFeature(FEATURE_HD_SPLIT) && version >= FEATURE_HD_SPLIT && version < FEATURE_PRE_SPLIT_KEYPOOL) { error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified."); return false; } + // Permanently upgrade to the version + SetMinVersion(GetClosestWalletFeature(version)); + for (auto spk_man : GetActiveScriptPubKeyMans()) { - if (!spk_man->Upgrade(prev_version, error)) { + if (!spk_man->Upgrade(prev_version, version, error)) { return false; } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 00e0e3c84d..69cf6b66a4 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -636,9 +636,6 @@ private: //! the current wallet version: clients below this version are not able to load the wallet int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE}; - //! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded - int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE; - int64_t nNextResend = 0; bool fBroadcastTransactions = false; // Local time that the tip block was received. Used to schedule wallet rebroadcasts. @@ -800,8 +797,8 @@ public: const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } + //! check whether we support the named feature + bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); } /** * populate vCoins with vector of available COutputs. @@ -853,7 +850,7 @@ public: //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; } /** * Adds a destination data tuple to the store, and saves it to disk @@ -1076,11 +1073,8 @@ public: unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower - void SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false) override; - - //! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) - bool SetMaxVersion(int nVersion); + //! signify that a particular wallet feature is now used. + void SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr) override; //! get the current wallet format (the oldest client version guaranteed to understand this wallet) int GetVersion() const { LOCK(cs_wallet); return nWalletVersion; } @@ -1201,7 +1195,7 @@ public: }; /** Upgrade the wallet */ - bool UpgradeWallet(int version, bilingual_str& error, std::vector<bilingual_str>& warnings); + bool UpgradeWallet(int version, bilingual_str& error); //! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers std::set<ScriptPubKeyMan*> GetActiveScriptPubKeyMans() const; diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index 2f3e597b90..702293e6c7 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -49,28 +49,51 @@ std::vector<fs::path> ListWalletDir() continue; } - // Get wallet path relative to walletdir by removing walletdir from the wallet path. - // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60. - const fs::path path = it->path().string().substr(offset); + try { + // Get wallet path relative to walletdir by removing walletdir from the wallet path. + // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60. + const fs::path path = it->path().string().substr(offset); - if (it->status().type() == fs::directory_file && - (ExistsBerkeleyDatabase(it->path()) || ExistsSQLiteDatabase(it->path()))) { - // Found a directory which contains wallet.dat btree file, add it as a wallet. - paths.emplace_back(path); - } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && ExistsBerkeleyDatabase(it->path())) { - if (it->path().filename() == "wallet.dat") { - // Found top-level wallet.dat btree file, add top level directory "" - // as a wallet. - paths.emplace_back(); - } else { - // Found top-level btree file not called wallet.dat. Current bitcoin - // software will never create these files but will allow them to be - // opened in a shared database environment for backwards compatibility. - // Add it to the list of available wallets. + if (it->status().type() == fs::directory_file && + (ExistsBerkeleyDatabase(it->path()) || ExistsSQLiteDatabase(it->path()))) { + // Found a directory which contains wallet.dat btree file, add it as a wallet. paths.emplace_back(path); + } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && ExistsBerkeleyDatabase(it->path())) { + if (it->path().filename() == "wallet.dat") { + // Found top-level wallet.dat btree file, add top level directory "" + // as a wallet. + paths.emplace_back(); + } else { + // Found top-level btree file not called wallet.dat. Current bitcoin + // software will never create these files but will allow them to be + // opened in a shared database environment for backwards compatibility. + // Add it to the list of available wallets. + paths.emplace_back(path); + } } + } catch (const std::exception& e) { + LogPrintf("%s: Error scanning %s: %s\n", __func__, it->path().string(), e.what()); + it.no_push(); } } return paths; } + +bool IsFeatureSupported(int wallet_version, int feature_version) +{ + return wallet_version >= feature_version; +} + +WalletFeature GetClosestWalletFeature(int version) +{ + if (version >= FEATURE_LATEST) return FEATURE_LATEST; + if (version >= FEATURE_PRE_SPLIT_KEYPOOL) return FEATURE_PRE_SPLIT_KEYPOOL; + if (version >= FEATURE_NO_DEFAULT_KEY) return FEATURE_NO_DEFAULT_KEY; + if (version >= FEATURE_HD_SPLIT) return FEATURE_HD_SPLIT; + if (version >= FEATURE_HD) return FEATURE_HD; + if (version >= FEATURE_COMPRPUBKEY) return FEATURE_COMPRPUBKEY; + if (version >= FEATURE_WALLETCRYPT) return FEATURE_WALLETCRYPT; + if (version >= FEATURE_BASE) return FEATURE_BASE; + return static_cast<WalletFeature>(0); +} diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index afdcb2e18a..27521abd81 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -29,7 +29,8 @@ enum WalletFeature FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL }; - +bool IsFeatureSupported(int wallet_version, int feature_version); +WalletFeature GetClosestWalletFeature(int version); enum WalletFlags : uint64_t { // wallet flags in the upper section (> 1 << 31) will lead to not opening the wallet if flag is unknown |