/ src / wallet / test / util.cpp
util.cpp
  1  // Copyright (c) 2021-2022 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          std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
 35          assert(desc);
 36          WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
 37          if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
 38      }
 39      WalletRescanReserver reserver(*wallet);
 40      reserver.reserve();
 41      CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
 42      assert(result.status == CWallet::ScanResult::SUCCESS);
 43      assert(result.last_scanned_block == cchain.Tip()->GetBlockHash());
 44      assert(*result.last_scanned_height == cchain.Height());
 45      assert(result.last_failed_block.IsNull());
 46      return wallet;
 47  }
 48  
 49  std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
 50  {
 51      bilingual_str error;
 52      std::vector<bilingual_str> warnings;
 53      auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings);
 54      NotifyWalletLoaded(context, wallet);
 55      if (context.chain) {
 56          wallet->postInitProcess();
 57      }
 58      return wallet;
 59  }
 60  
 61  std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
 62  {
 63      DatabaseOptions options;
 64      options.create_flags = WALLET_FLAG_DESCRIPTORS;
 65      DatabaseStatus status;
 66      bilingual_str error;
 67      std::vector<bilingual_str> warnings;
 68      auto database = MakeWalletDatabase("", options, status, error);
 69      return TestLoadWallet(std::move(database), context, options.create_flags);
 70  }
 71  
 72  void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
 73  {
 74      // Calls SyncWithValidationInterfaceQueue
 75      wallet->chain().waitForNotificationsIfTipChanged({});
 76      wallet->m_chain_notifications_handler.reset();
 77      UnloadWallet(std::move(wallet));
 78  }
 79  
 80  std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
 81  {
 82      return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
 83  }
 84  
 85  std::string getnewaddress(CWallet& w)
 86  {
 87      constexpr auto output_type = OutputType::BECH32;
 88      return EncodeDestination(getNewDestination(w, output_type));
 89  }
 90  
 91  CTxDestination getNewDestination(CWallet& w, OutputType output_type)
 92  {
 93      return *Assert(w.GetNewDestination(output_type, ""));
 94  }
 95  
 96  // BytePrefix compares equality with other byte spans that begin with the same prefix.
 97  struct BytePrefix { Span<const std::byte> prefix; };
 98  bool operator<(BytePrefix a, Span<const std::byte> b) { return a.prefix < b.subspan(0, std::min(a.prefix.size(), b.size())); }
 99  bool operator<(Span<const std::byte> a, BytePrefix b) { return a.subspan(0, std::min(a.size(), b.prefix.size())) < b.prefix; }
100  
101  MockableCursor::MockableCursor(const MockableData& records, bool pass, Span<const std::byte> prefix)
102  {
103      m_pass = pass;
104      std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
105  }
106  
107  DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
108  {
109      if (!m_pass) {
110          return Status::FAIL;
111      }
112      if (m_cursor == m_cursor_end) {
113          return Status::DONE;
114      }
115      key.clear();
116      value.clear();
117      const auto& [key_data, value_data] = *m_cursor;
118      key.write(key_data);
119      value.write(value_data);
120      m_cursor++;
121      return Status::MORE;
122  }
123  
124  bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
125  {
126      if (!m_pass) {
127          return false;
128      }
129      SerializeData key_data{key.begin(), key.end()};
130      const auto& it = m_records.find(key_data);
131      if (it == m_records.end()) {
132          return false;
133      }
134      value.clear();
135      value.write(it->second);
136      return true;
137  }
138  
139  bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
140  {
141      if (!m_pass) {
142          return false;
143      }
144      SerializeData key_data{key.begin(), key.end()};
145      SerializeData value_data{value.begin(), value.end()};
146      auto [it, inserted] = m_records.emplace(key_data, value_data);
147      if (!inserted && overwrite) { // Overwrite if requested
148          it->second = value_data;
149          inserted = true;
150      }
151      return inserted;
152  }
153  
154  bool MockableBatch::EraseKey(DataStream&& key)
155  {
156      if (!m_pass) {
157          return false;
158      }
159      SerializeData key_data{key.begin(), key.end()};
160      m_records.erase(key_data);
161      return true;
162  }
163  
164  bool MockableBatch::HasKey(DataStream&& key)
165  {
166      if (!m_pass) {
167          return false;
168      }
169      SerializeData key_data{key.begin(), key.end()};
170      return m_records.count(key_data) > 0;
171  }
172  
173  bool MockableBatch::ErasePrefix(Span<const std::byte> prefix)
174  {
175      if (!m_pass) {
176          return false;
177      }
178      auto it = m_records.begin();
179      while (it != m_records.end()) {
180          auto& key = it->first;
181          if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
182              it++;
183              continue;
184          }
185          it = m_records.erase(it);
186      }
187      return true;
188  }
189  
190  std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records)
191  {
192      return std::make_unique<MockableDatabase>(records);
193  }
194  
195  MockableDatabase& GetMockableDatabase(CWallet& wallet)
196  {
197      return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
198  }
199  } // namespace wallet