/ src / wallet / test / util.cpp
util.cpp
  1  // Copyright (c) 2021-present The Bitcoin Core 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      }
 27      {
 28          LOCK(wallet->cs_wallet);
 29          wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
 30          wallet->SetupDescriptorScriptPubKeyMans();
 31  
 32          FlatSigningProvider provider;
 33          std::string error;
 34          auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
 35          assert(descs.size() == 1);
 36          auto& desc = descs.at(0);
 37          WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
 38          Assert(wallet->AddWalletDescriptor(w_desc, provider, "", false));
 39      }
 40      WalletRescanReserver reserver(*wallet);
 41      reserver.reserve();
 42      CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
 43      assert(result.status == CWallet::ScanResult::SUCCESS);
 44      assert(result.last_scanned_block == cchain.Tip()->GetBlockHash());
 45      assert(*result.last_scanned_height == cchain.Height());
 46      assert(result.last_failed_block.IsNull());
 47      return wallet;
 48  }
 49  
 50  std::shared_ptr<CWallet> TestCreateWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
 51  {
 52      bilingual_str _error;
 53      std::vector<bilingual_str> _warnings;
 54      auto wallet = CWallet::CreateNew(context, "", std::move(database), create_flags, _error, _warnings);
 55      NotifyWalletLoaded(context, wallet);
 56      if (context.chain) {
 57          wallet->postInitProcess();
 58      }
 59      return wallet;
 60  }
 61  
 62  std::shared_ptr<CWallet> TestCreateWallet(WalletContext& context)
 63  {
 64      DatabaseOptions options;
 65      options.require_create = true;
 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 TestCreateWallet(std::move(database), context, options.create_flags);
 72  }
 73  
 74  
 75  std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context)
 76  {
 77      bilingual_str error;
 78      std::vector<bilingual_str> warnings;
 79      auto wallet = CWallet::LoadExisting(context, "", std::move(database), error, warnings);
 80      NotifyWalletLoaded(context, wallet);
 81      if (context.chain) {
 82          wallet->postInitProcess();
 83      }
 84      return wallet;
 85  }
 86  
 87  std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
 88  {
 89      DatabaseOptions options;
 90      options.require_existing = true;
 91      DatabaseStatus status;
 92      bilingual_str error;
 93      std::vector<bilingual_str> warnings;
 94      auto database = MakeWalletDatabase("", options, status, error);
 95      return TestLoadWallet(std::move(database), context);
 96  }
 97  
 98  void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
 99  {
100      // Calls SyncWithValidationInterfaceQueue
101      wallet->chain().waitForNotificationsIfTipChanged({});
102      wallet->m_chain_notifications_handler.reset();
103      WaitForDeleteWallet(std::move(wallet));
104  }
105  
106  std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
107  {
108      return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
109  }
110  
111  std::string getnewaddress(CWallet& w)
112  {
113      constexpr auto output_type = OutputType::BECH32;
114      return EncodeDestination(getNewDestination(w, output_type));
115  }
116  
117  CTxDestination getNewDestination(CWallet& w, OutputType output_type)
118  {
119      return *Assert(w.GetNewDestination(output_type, ""));
120  }
121  
122  MockableCursor::MockableCursor(const MockableData& records, bool pass, std::span<const std::byte> prefix)
123  {
124      m_pass = pass;
125      std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
126  }
127  
128  DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
129  {
130      if (!m_pass) {
131          return Status::FAIL;
132      }
133      if (m_cursor == m_cursor_end) {
134          return Status::DONE;
135      }
136      key.clear();
137      value.clear();
138      const auto& [key_data, value_data] = *m_cursor;
139      key.write(key_data);
140      value.write(value_data);
141      m_cursor++;
142      return Status::MORE;
143  }
144  
145  bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
146  {
147      if (!m_pass) {
148          return false;
149      }
150      SerializeData key_data{key.begin(), key.end()};
151      const auto& it = m_records.find(key_data);
152      if (it == m_records.end()) {
153          return false;
154      }
155      value.clear();
156      value.write(it->second);
157      return true;
158  }
159  
160  bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
161  {
162      if (!m_pass) {
163          return false;
164      }
165      SerializeData key_data{key.begin(), key.end()};
166      SerializeData value_data{value.begin(), value.end()};
167      auto [it, inserted] = m_records.emplace(key_data, value_data);
168      if (!inserted && overwrite) { // Overwrite if requested
169          it->second = value_data;
170          inserted = true;
171      }
172      return inserted;
173  }
174  
175  bool MockableBatch::EraseKey(DataStream&& key)
176  {
177      if (!m_pass) {
178          return false;
179      }
180      SerializeData key_data{key.begin(), key.end()};
181      m_records.erase(key_data);
182      return true;
183  }
184  
185  bool MockableBatch::HasKey(DataStream&& key)
186  {
187      if (!m_pass) {
188          return false;
189      }
190      SerializeData key_data{key.begin(), key.end()};
191      return m_records.contains(key_data);
192  }
193  
194  bool MockableBatch::ErasePrefix(std::span<const std::byte> prefix)
195  {
196      if (!m_pass) {
197          return false;
198      }
199      auto it = m_records.begin();
200      while (it != m_records.end()) {
201          auto& key = it->first;
202          if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
203              it++;
204              continue;
205          }
206          it = m_records.erase(it);
207      }
208      return true;
209  }
210  
211  std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records)
212  {
213      return std::make_unique<MockableDatabase>(records);
214  }
215  
216  MockableDatabase& GetMockableDatabase(CWallet& wallet)
217  {
218      return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
219  }
220  
221  wallet::DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
222  {
223      keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
224  
225      FlatSigningProvider keys;
226      std::string error;
227      auto parsed_descs = Parse(desc_str, keys, error, false);
228      Assert(success == (!parsed_descs.empty()));
229      if (!success) return nullptr;
230      auto& desc = parsed_descs.at(0);
231  
232      const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
233  
234      WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
235  
236      LOCK(keystore.cs_wallet);
237      auto spkm = Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
238      return &spkm.value().get();
239  };
240  } // namespace wallet