util.cpp raw

   1  // Copyright (c) 2021-2022 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <wallet/test/util.h>
   6  
   7  #include <chain.h>
   8  #include <key.h>
   9  #include <key_io.h>
  10  #include <streams.h>
  11  #include <test/util/setup_common.h>
  12  #include <validationinterface.h>
  13  #include <wallet/context.h>
  14  #include <wallet/wallet.h>
  15  #include <wallet/walletdb.h>
  16  
  17  #include <memory>
  18  
  19  namespace wallet {
  20  std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
  21  {
  22      auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockableWalletDatabase());
  23      {
  24          LOCK2(wallet->cs_wallet, ::cs_main);
  25          wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
  26          wallet->m_default_address_type = OutputType::BECH32M;
  27      }
  28      {
  29          LOCK(wallet->cs_wallet);
  30          wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
  31          wallet->SetupDescriptorScriptPubKeyMans();
  32  
  33          FlatSigningProvider provider;
  34          std::string error;
  35          auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
  36          assert(descs.size() == 1);
  37          auto& desc = descs.at(0);
  38          WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
  39          if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
  40      }
  41      WalletRescanReserver reserver(*wallet);
  42      reserver.reserve();
  43      CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
  44      assert(result.status == CWallet::ScanResult::SUCCESS);
  45      assert(result.last_scanned_block == cchain.Tip()->GetBlockHash());
  46      assert(*result.last_scanned_height == cchain.Height());
  47      assert(result.last_failed_block.IsNull());
  48      return wallet;
  49  }
  50  
  51  std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
  52  {
  53      bilingual_str error;
  54      std::vector<bilingual_str> warnings;
  55      auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings);
  56      NotifyWalletLoaded(context, wallet);
  57      if (context.chain) {
  58          wallet->postInitProcess();
  59      }
  60      return wallet;
  61  }
  62  
  63  std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
  64  {
  65      DatabaseOptions options;
  66      options.create_flags = WALLET_FLAG_DESCRIPTORS;
  67      DatabaseStatus status;
  68      bilingual_str error;
  69      std::vector<bilingual_str> warnings;
  70      auto database = MakeWalletDatabase("", options, status, error);
  71      return TestLoadWallet(std::move(database), context, options.create_flags);
  72  }
  73  
  74  void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
  75  {
  76      // Calls SyncWithValidationInterfaceQueue
  77      wallet->chain().waitForNotificationsIfTipChanged({});
  78      wallet->m_chain_notifications_handler.reset();
  79      WaitForDeleteWallet(std::move(wallet));
  80  }
  81  
  82  std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
  83  {
  84      return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
  85  }
  86  
  87  std::string getnewaddress(CWallet& w)
  88  {
  89      constexpr auto output_type = OutputType::BECH32;
  90      return EncodeDestination(getNewDestination(w, output_type));
  91  }
  92  
  93  CTxDestination getNewDestination(CWallet& w, OutputType output_type)
  94  {
  95      return *Assert(w.GetNewDestination(output_type, ""));
  96  }
  97  
  98  MockableCursor::MockableCursor(const MockableData& records, bool pass, Span<const std::byte> prefix)
  99  {
 100      m_pass = pass;
 101      std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
 102  }
 103  
 104  DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
 105  {
 106      if (!m_pass) {
 107          return Status::FAIL;
 108      }
 109      if (m_cursor == m_cursor_end) {
 110          return Status::DONE;
 111      }
 112      key.clear();
 113      value.clear();
 114      const auto& [key_data, value_data] = *m_cursor;
 115      key.write(key_data);
 116      value.write(value_data);
 117      m_cursor++;
 118      return Status::MORE;
 119  }
 120  
 121  bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
 122  {
 123      if (!m_pass) {
 124          return false;
 125      }
 126      SerializeData key_data{key.begin(), key.end()};
 127      const auto& it = m_records.find(key_data);
 128      if (it == m_records.end()) {
 129          return false;
 130      }
 131      value.clear();
 132      value.write(it->second);
 133      return true;
 134  }
 135  
 136  bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
 137  {
 138      if (!m_pass) {
 139          return false;
 140      }
 141      SerializeData key_data{key.begin(), key.end()};
 142      SerializeData value_data{value.begin(), value.end()};
 143      auto [it, inserted] = m_records.emplace(key_data, value_data);
 144      if (!inserted && overwrite) { // Overwrite if requested
 145          it->second = value_data;
 146          inserted = true;
 147      }
 148      return inserted;
 149  }
 150  
 151  bool MockableBatch::EraseKey(DataStream&& key)
 152  {
 153      if (!m_pass) {
 154          return false;
 155      }
 156      SerializeData key_data{key.begin(), key.end()};
 157      m_records.erase(key_data);
 158      return true;
 159  }
 160  
 161  bool MockableBatch::HasKey(DataStream&& key)
 162  {
 163      if (!m_pass) {
 164          return false;
 165      }
 166      SerializeData key_data{key.begin(), key.end()};
 167      return m_records.count(key_data) > 0;
 168  }
 169  
 170  bool MockableBatch::ErasePrefix(Span<const std::byte> prefix)
 171  {
 172      if (!m_pass) {
 173          return false;
 174      }
 175      auto it = m_records.begin();
 176      while (it != m_records.end()) {
 177          auto& key = it->first;
 178          if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
 179              it++;
 180              continue;
 181          }
 182          it = m_records.erase(it);
 183      }
 184      return true;
 185  }
 186  
 187  std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records)
 188  {
 189      return std::make_unique<MockableDatabase>(records);
 190  }
 191  
 192  MockableDatabase& GetMockableDatabase(CWallet& wallet)
 193  {
 194      return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
 195  }
 196  
 197  wallet::ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
 198  {
 199      keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
 200  
 201      FlatSigningProvider keys;
 202      std::string error;
 203      auto parsed_descs = Parse(desc_str, keys, error, false);
 204      Assert(success == (!parsed_descs.empty()));
 205      if (!success) return nullptr;
 206      auto& desc = parsed_descs.at(0);
 207  
 208      const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
 209  
 210      WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
 211  
 212      LOCK(keystore.cs_wallet);
 213  
 214      return Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
 215  };
 216  } // namespace wallet
 217