LCOV - code coverage report
Current view: top level - src/wallet/test - walletload_tests.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 0 125 0.0 %
Date: 2023-10-05 12:38:51 Functions: 0 42 0.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2022 The Bitcoin Core developers
       2                 :            : // Distributed under the MIT software license, see the accompanying
       3                 :            : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       4                 :            : 
       5                 :            : #include <wallet/test/util.h>
       6                 :            : #include <wallet/wallet.h>
       7                 :            : #include <test/util/logging.h>
       8                 :            : #include <test/util/setup_common.h>
       9                 :            : 
      10                 :            : #include <boost/test/unit_test.hpp>
      11                 :            : 
      12                 :            : namespace wallet {
      13                 :            : 
      14                 :          0 : BOOST_AUTO_TEST_SUITE(walletload_tests)
      15                 :            : 
      16                 :            : class DummyDescriptor final : public Descriptor {
      17                 :          0 : private:
      18                 :          0 :     std::string desc;
      19                 :            : public:
      20                 :          0 :     explicit DummyDescriptor(const std::string& descriptor) : desc(descriptor) {};
      21                 :          0 :     ~DummyDescriptor() = default;
      22                 :            : 
      23                 :          0 :     std::string ToString(bool compat_format) const override { return desc; }
      24                 :          0 :     std::optional<OutputType> GetOutputType() const override { return OutputType::UNKNOWN; }
      25                 :            : 
      26                 :          0 :     bool IsRange() const override { return false; }
      27                 :          0 :     bool IsSolvable() const override { return false; }
      28                 :          0 :     bool IsSingleType() const override { return true; }
      29                 :          0 :     bool ToPrivateString(const SigningProvider& provider, std::string& out) const override { return false; }
      30                 :          0 :     bool ToNormalizedString(const SigningProvider& provider, std::string& out, const DescriptorCache* cache = nullptr) const override { return false; }
      31                 :          0 :     bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const override { return false; };
      32                 :          0 :     bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override { return false; }
      33                 :          0 :     void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const override {}
      34                 :          0 :     std::optional<int64_t> ScriptSize() const override { return {}; }
      35                 :          0 :     std::optional<int64_t> MaxSatisfactionWeight(bool) const override { return {}; }
      36                 :          0 :     std::optional<int64_t> MaxSatisfactionElems() const override { return {}; }
      37                 :            : };
      38                 :            : 
      39                 :          0 : BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup)
      40                 :            : {
      41                 :          0 :     std::unique_ptr<WalletDatabase> database = CreateMockableWalletDatabase();
      42                 :            :     {
      43                 :            :         // Write unknown active descriptor
      44                 :          0 :         WalletBatch batch(*database, false);
      45                 :          0 :         std::string unknown_desc = "trx(tpubD6NzVbkrYhZ4Y4S7m6Y5s9GD8FqEMBy56AGphZXuagajudVZEnYyBahZMgHNCTJc2at82YX6s8JiL1Lohu5A3v1Ur76qguNH4QVQ7qYrBQx/86'/1'/0'/0/*)#8pn8tzdt";
      46                 :          0 :         WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(unknown_desc), 0, 0, 0, 0);
      47                 :          0 :         BOOST_CHECK(batch.WriteDescriptor(uint256(), wallet_descriptor));
      48                 :          0 :         BOOST_CHECK(batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(OutputType::UNKNOWN), uint256(), false));
      49                 :          0 :     }
      50                 :            : 
      51                 :            :     {
      52                 :            :         // Now try to load the wallet and verify the error.
      53                 :          0 :         const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database)));
      54                 :          0 :         BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::UNKNOWN_DESCRIPTOR);
      55                 :          0 :     }
      56                 :            : 
      57                 :            :     // Test 2
      58                 :            :     // Now write a valid descriptor with an invalid ID.
      59                 :            :     // As the software produces another ID for the descriptor, the loading process must be aborted.
      60                 :          0 :     database = CreateMockableWalletDatabase();
      61                 :            : 
      62                 :            :     // Verify the error
      63                 :          0 :     bool found = false;
      64                 :          0 :     DebugLogHelper logHelper("The descriptor ID calculated by the wallet differs from the one in DB", [&](const std::string* s) {
      65                 :          0 :         found = true;
      66                 :          0 :         return false;
      67                 :            :     });
      68                 :            : 
      69                 :            :     {
      70                 :            :         // Write valid descriptor with invalid ID
      71                 :          0 :         WalletBatch batch(*database, false);
      72                 :          0 :         std::string desc = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
      73                 :          0 :         WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(desc), 0, 0, 0, 0);
      74                 :          0 :         BOOST_CHECK(batch.WriteDescriptor(uint256::ONE, wallet_descriptor));
      75                 :          0 :     }
      76                 :            : 
      77                 :            :     {
      78                 :            :         // Now try to load the wallet and verify the error.
      79                 :          0 :         const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database)));
      80                 :          0 :         BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
      81                 :          0 :         BOOST_CHECK(found); // The error must be logged
      82                 :          0 :     }
      83                 :          0 : }
      84                 :            : 
      85                 :          0 : bool HasAnyRecordOfType(WalletDatabase& db, const std::string& key)
      86                 :            : {
      87                 :          0 :     std::unique_ptr<DatabaseBatch> batch = db.MakeBatch(false);
      88                 :          0 :     BOOST_CHECK(batch);
      89                 :          0 :     std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
      90                 :          0 :     BOOST_CHECK(cursor);
      91                 :          0 :     while (true) {
      92                 :          0 :         DataStream ssKey{};
      93                 :          0 :         DataStream ssValue{};
      94                 :          0 :         DatabaseCursor::Status status = cursor->Next(ssKey, ssValue);
      95                 :          0 :         assert(status != DatabaseCursor::Status::FAIL);
      96                 :          0 :         if (status == DatabaseCursor::Status::DONE) break;
      97                 :          0 :         std::string type;
      98                 :          0 :         ssKey >> type;
      99                 :          0 :         if (type == key) return true;
     100                 :          0 :     }
     101                 :          0 :     return false;
     102                 :          0 : }
     103                 :            : 
     104                 :            : template<typename... Args>
     105                 :          0 : SerializeData MakeSerializeData(const Args&... args)
     106                 :            : {
     107                 :          0 :     CDataStream s(0, 0);
     108                 :          0 :     SerializeMany(s, args...);
     109                 :          0 :     return {s.begin(), s.end()};
     110                 :          0 : }
     111                 :            : 
     112                 :            : 
     113                 :          0 : BOOST_FIXTURE_TEST_CASE(wallet_load_ckey, TestingSetup)
     114                 :            : {
     115                 :          0 :     SerializeData ckey_record_key;
     116                 :          0 :     SerializeData ckey_record_value;
     117                 :          0 :     MockableData records;
     118                 :            : 
     119                 :            :     {
     120                 :            :         // Context setup.
     121                 :            :         // Create and encrypt legacy wallet
     122                 :          0 :         std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase()));
     123                 :          0 :         LOCK(wallet->cs_wallet);
     124                 :          0 :         auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan();
     125                 :          0 :         BOOST_CHECK(legacy_spkm->SetupGeneration(true));
     126                 :            : 
     127                 :            :         // Retrieve a key
     128                 :          0 :         CTxDestination dest = *Assert(legacy_spkm->GetNewDestination(OutputType::LEGACY));
     129                 :          0 :         CKeyID key_id = GetKeyForDestination(*legacy_spkm, dest);
     130                 :          0 :         CKey first_key;
     131                 :          0 :         BOOST_CHECK(legacy_spkm->GetKey(key_id, first_key));
     132                 :            : 
     133                 :            :         // Encrypt the wallet
     134                 :          0 :         BOOST_CHECK(wallet->EncryptWallet("encrypt"));
     135                 :          0 :         wallet->Flush();
     136                 :            : 
     137                 :            :         // Store a copy of all the records
     138                 :          0 :         records = GetMockableDatabase(*wallet).m_records;
     139                 :            : 
     140                 :            :         // Get the record for the retrieved key
     141                 :          0 :         ckey_record_key = MakeSerializeData(DBKeys::CRYPTED_KEY, first_key.GetPubKey());
     142                 :          0 :         ckey_record_value = records.at(ckey_record_key);
     143                 :          0 :     }
     144                 :            : 
     145                 :            :     {
     146                 :            :         // First test case:
     147                 :            :         // Erase all the crypted keys from db and unlock the wallet.
     148                 :            :         // The wallet will only re-write the crypted keys to db if any checksum is missing at load time.
     149                 :            :         // So, if any 'ckey' record re-appears on db, then the checksums were not properly calculated, and we are re-writing
     150                 :            :         // the records every time that 'CWallet::Unlock' gets called, which is not good.
     151                 :            : 
     152                 :            :         // Load the wallet and check that is encrypted
     153                 :          0 :         std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
     154                 :          0 :         BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
     155                 :          0 :         BOOST_CHECK(wallet->IsCrypted());
     156                 :          0 :         BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
     157                 :            : 
     158                 :            :         // Now delete all records and check that the 'Unlock' function doesn't re-write them
     159                 :          0 :         BOOST_CHECK(wallet->GetLegacyScriptPubKeyMan()->DeleteRecords());
     160                 :          0 :         BOOST_CHECK(!HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
     161                 :          0 :         BOOST_CHECK(wallet->Unlock("encrypt"));
     162                 :          0 :         BOOST_CHECK(!HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
     163                 :          0 :     }
     164                 :          0 : 
     165                 :          0 :     {
     166                 :          0 :         // Second test case:
     167                 :          0 :         // Verify that loading up a 'ckey' with no checksum triggers a complete re-write of the crypted keys.
     168                 :          0 : 
     169                 :          0 :         // Cut off the 32 byte checksum from a ckey record
     170                 :          0 :         records[ckey_record_key].resize(ckey_record_value.size() - 32);
     171                 :            : 
     172                 :            :         // Load the wallet and check that is encrypted
     173                 :          0 :         std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
     174                 :          0 :         BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::LOAD_OK);
     175                 :          0 :         BOOST_CHECK(wallet->IsCrypted());
     176                 :          0 :         BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
     177                 :            : 
     178                 :            :         // Now delete all ckey records and check that the 'Unlock' function re-writes them
     179                 :            :         // (this is because the wallet, at load time, found a ckey record with no checksum)
     180                 :          0 :         BOOST_CHECK(wallet->GetLegacyScriptPubKeyMan()->DeleteRecords());
     181                 :          0 :         BOOST_CHECK(!HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
     182                 :          0 :         BOOST_CHECK(wallet->Unlock("encrypt"));
     183                 :          0 :         BOOST_CHECK(HasAnyRecordOfType(wallet->GetDatabase(), DBKeys::CRYPTED_KEY));
     184                 :          0 :     }
     185                 :            : 
     186                 :            :     {
     187                 :            :         // Third test case:
     188                 :            :         // Verify that loading up a 'ckey' with an invalid checksum throws an error.
     189                 :            : 
     190                 :            :         // Cut off the 32 byte checksum from a ckey record
     191                 :          0 :         records[ckey_record_key].resize(ckey_record_value.size() - 32);
     192                 :            :         // Fill in the checksum space with 0s
     193                 :          0 :         records[ckey_record_key].resize(ckey_record_value.size());
     194                 :            : 
     195                 :          0 :         std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
     196                 :          0 :         BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
     197                 :          0 :     }
     198                 :            : 
     199                 :            :     {
     200                 :            :         // Fourth test case:
     201                 :            :         // Verify that loading up a 'ckey' with an invalid pubkey throws an error
     202                 :          0 :         CPubKey invalid_key;
     203                 :          0 :         BOOST_CHECK(!invalid_key.IsValid());
     204                 :          0 :         SerializeData key = MakeSerializeData(DBKeys::CRYPTED_KEY, invalid_key);
     205                 :          0 :         records[key] = ckey_record_value;
     206                 :            : 
     207                 :          0 :         std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", CreateMockableWalletDatabase(records)));
     208                 :          0 :         BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT);
     209                 :          0 :     }
     210                 :          0 : }
     211                 :            : 
     212                 :          0 : BOOST_AUTO_TEST_SUITE_END()
     213                 :            : } // namespace wallet

Generated by: LCOV version 1.14