Bitcoin Core  31.0.0
P2P Digital Currency
util.cpp
Go to the documentation of this file.
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);
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;
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 
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 
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 
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 
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 
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 
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
void write(std::span< const value_type > src)
Definition: streams.h:236
static UniValue Parse(std::string_view raw, ParamFormat format=ParamFormat::JSON)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition: client.cpp:395
Status Next(DataStream &key, DataStream &value) override
Definition: util.cpp:128
static std::shared_ptr< CWallet > LoadExisting(WalletContext &context, const std::string &name, std::unique_ptr< WalletDatabase > database, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:3127
assert(!tx.IsCoinBase())
Bilingual messages:
Definition: translation.h:24
An in-memory indexed chain of blocks.
Definition: chain.h:379
std::map< SerializeData, SerializeData, std::less<> > MockableData
Definition: util.h:49
const char * prefix
Definition: rest.cpp:1141
int Height() const
Return the maximal height in the chain.
Definition: chain.h:425
RecursiveMutex cs_wallet
Main wallet lock.
Definition: wallet.h:460
MockableCursor(const MockableData &records, bool pass)
Definition: util.h:58
CBlockIndex * Genesis() const
Returns the index entry for the genesis block of this chain, or nullptr if none.
Definition: chain.h:390
MockableData::const_iterator m_cursor
Definition: util.h:54
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:1087
std::shared_ptr< CWallet > TestLoadWallet(std::unique_ptr< WalletDatabase > database, WalletContext &context)
Definition: util.cpp:75
bool reserve(bool with_passphrase=false)
Definition: wallet.h:1098
CTxDestination getNewDestination(CWallet &w, OutputType output_type)
Returns a new destination, of an specific type, from the wallet.
Definition: util.cpp:117
std::unique_ptr< WalletDatabase > DuplicateMockDatabase(WalletDatabase &database)
Definition: util.cpp:106
MockableData::const_iterator m_cursor_end
Definition: util.h:55
MockableData & m_records
Definition: util.h:68
OutputType
Definition: outputtype.h:18
uint256 GetBlockHash() const
Definition: chain.h:198
#define LOCK2(cs1, cs2)
Definition: sync.h:259
void WaitForDeleteWallet(std::shared_ptr< CWallet > &&wallet)
Explicitly delete the wallet.
Definition: wallet.cpp:254
uint64_t create_flags
Definition: db.h:176
std::shared_ptr< CWallet > TestCreateWallet(std::unique_ptr< WalletDatabase > database, WalletContext &context, uint64_t create_flags)
Definition: util.cpp:50
wallet::DescriptorScriptPubKeyMan * CreateDescriptor(CWallet &keystore, const std::string &desc_str, const bool success)
Definition: util.cpp:221
#define LOCK(cs)
Definition: sync.h:258
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:132
std::vector< std::byte, zero_after_free_allocator< std::byte > > SerializeData
Byte-vector that clears its contents before deletion.
Definition: zeroafterfree.h:44
util::Result< std::reference_wrapper< DescriptorScriptPubKeyMan > > AddWalletDescriptor(WalletDescriptor &desc, const FlatSigningProvider &signing_provider, const std::string &label, bool internal) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Add a descriptor to the wallet, return a ScriptPubKeyMan & associated output type.
Definition: wallet.cpp:3756
bool EraseKey(DataStream &&key) override
Definition: util.cpp:175
void NotifyWalletLoaded(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
Definition: wallet.cpp:223
static std::shared_ptr< CWallet > CreateNew(WalletContext &context, const std::string &name, std::unique_ptr< WalletDatabase > database, uint64_t wallet_creation_flags, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:3073
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:53
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:309
MockableDatabase & GetMockableDatabase(CWallet &wallet)
Definition: util.cpp:216
Descriptor with some wallet metadata.
Definition: walletutil.h:63
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
Definition: util.cpp:160
DatabaseStatus
Definition: db.h:186
std::unique_ptr< CWallet > CreateSyncedWallet(interfaces::Chain &chain, CChain &cchain, const CKey &key)
Definition: util.cpp:20
std::unique_ptr< WalletDatabase > CreateMockableWalletDatabase(MockableData records)
Definition: util.cpp:211
A WalletDatabase whose contents and return values can be modified as needed for testing.
Definition: util.h:98
void clear()
Definition: streams.h:173
auto result
Definition: common-types.h:74
Interface giving clients (wallet processes, maybe other analysis tools in the future) ability to acce...
Definition: chain.h:117
void TestUnloadWallet(std::shared_ptr< CWallet > &&wallet)
Definition: util.cpp:98
bool ReadKey(DataStream &&key, DataStream &value) override
Definition: util.cpp:145
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
Definition: addresstype.h:143
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:396
WalletContext struct containing references to state shared between CWallet instances, like the reference to the chain interface, and the list of opened wallets.
Definition: context.h:36
bool require_existing
Definition: db.h:173
interfaces::Chain * chain
Definition: context.h:37
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:294
An encapsulated private key.
Definition: key.h:35
bool HasKey(DataStream &&key) override
Definition: util.cpp:185
RPCHelpMan getnewaddress()
Definition: addresses.cpp:21
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:231
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
util::Result< CTxDestination > GetNewDestination(OutputType type, const std::string &label)
Definition: wallet.cpp:2601
bool ErasePrefix(std::span< const std::byte > prefix) override
Definition: util.cpp:194
An instance of this class represents one database.
Definition: db.h:129
std::unique_ptr< WalletDatabase > MakeWalletDatabase(const std::string &name, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error_string)
Definition: wallet.cpp:2940
#define Assert(val)
Identity function.
Definition: check.h:113