Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-present The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <bitcoin-build-config.h> // IWYU pragma: keep
7 : :
8 : : #include <wallet/walletdb.h>
9 : :
10 : : #include <common/system.h>
11 : : #include <key_io.h>
12 : : #include <primitives/transaction_identifier.h>
13 : : #include <protocol.h>
14 : : #include <script/script.h>
15 : : #include <serialize.h>
16 : : #include <sync.h>
17 : : #include <util/bip32.h>
18 : : #include <util/check.h>
19 : : #include <util/fs.h>
20 : : #include <util/time.h>
21 : : #include <util/translation.h>
22 : : #include <wallet/migrate.h>
23 : : #include <wallet/sqlite.h>
24 : : #include <wallet/wallet.h>
25 : :
26 : : #include <atomic>
27 : : #include <optional>
28 : : #include <string>
29 : :
30 : : namespace wallet {
31 : : namespace DBKeys {
32 : : const std::string ACENTRY{"acentry"};
33 : : const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
34 : : const std::string ACTIVEINTERNALSPK{"activeinternalspk"};
35 : : const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
36 : : const std::string BESTBLOCK{"bestblock"};
37 : : const std::string CRYPTED_KEY{"ckey"};
38 : : const std::string CSCRIPT{"cscript"};
39 : : const std::string DEFAULTKEY{"defaultkey"};
40 : : const std::string DESTDATA{"destdata"};
41 : : const std::string FLAGS{"flags"};
42 : : const std::string HDCHAIN{"hdchain"};
43 : : const std::string KEYMETA{"keymeta"};
44 : : const std::string KEY{"key"};
45 : : const std::string LAST_DECRYPTED_FEATURES{"lastdecryptedfeatures"};
46 : : const std::string LAST_OPENED_FEATURES{"lastopenedfeatures"};
47 : : const std::string LOCKED_UTXO{"lockedutxo"};
48 : : const std::string MASTER_KEY{"mkey"};
49 : : const std::string MINVERSION{"minversion"};
50 : : const std::string NAME{"name"};
51 : : const std::string OLD_KEY{"wkey"};
52 : : const std::string ORDERPOSNEXT{"orderposnext"};
53 : : const std::string POOL{"pool"};
54 : : const std::string PURPOSE{"purpose"};
55 : : const std::string SETTINGS{"settings"};
56 : : const std::string TX{"tx"};
57 : : const std::string WTX_VARIANT{"wtxvariant"};
58 : : const std::string VERSION{"version"};
59 : : const std::string WALLETDESCRIPTOR{"walletdescriptor"};
60 : : const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"};
61 : : const std::string WALLETDESCRIPTORLHCACHE{"walletdescriptorlhcache"};
62 : : const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"};
63 : : const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"};
64 : : const std::string WATCHMETA{"watchmeta"};
65 : : const std::string WATCHS{"watchs"};
66 : : const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS};
67 : : } // namespace DBKeys
68 : :
69 : 5 : void LogDBInfo()
70 : : {
71 : : // Add useful DB information here. This will be printed during startup.
72 [ + - ]: 5 : LogInfo("Using SQLite Version %s", SQLiteDatabaseVersion());
73 : 5 : }
74 : :
75 : : //
76 : : // WalletBatch
77 : : //
78 : :
79 : 6230 : bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
80 : : {
81 [ + - ]: 18690 : return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
82 : : }
83 : :
84 : 0 : bool WalletBatch::EraseName(const std::string& strAddress)
85 : : {
86 : : // This should only be used for sending addresses, never for receiving addresses,
87 : : // receiving addresses must always have an address book entry if they're not change return.
88 [ # # ]: 0 : return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
89 : : }
90 : :
91 : 6230 : bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
92 : : {
93 [ + - ]: 18690 : return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
94 : : }
95 : :
96 : 0 : bool WalletBatch::ErasePurpose(const std::string& strAddress)
97 : : {
98 [ # # ]: 0 : return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
99 : : }
100 : :
101 : 830 : bool WalletBatch::WriteFullTx(const CWalletTx& wtx)
102 : : {
103 : 830 : const Txid txid = wtx.GetHash();
104 : : // Persist all witness variants. Including the canonical one
105 [ + + ]: 1660 : for (const auto& [wtxid, tx] : wtx.GetTxs()) {
106 [ + - ]: 830 : if (!WriteWtxVariant(txid, tx)) return false;
107 : : }
108 [ + - ]: 1660 : return WriteIC(std::make_pair(DBKeys::TX, txid), wtx);
109 : : }
110 : :
111 : 1 : bool WalletBatch::EraseTx(Txid hash)
112 : : {
113 [ + - + - ]: 2 : if (!EraseIC(std::make_pair(DBKeys::TX, hash.ToUint256()))) return false;
114 : : // Drop all witness variant records too, so none are left dangling
115 [ + - + - : 1 : return m_batch->ErasePrefix(DataStream() << DBKeys::WTX_VARIANT << hash);
- + + - ]
116 : : }
117 : :
118 : 830 : bool WalletBatch::WriteWtxVariant(const Txid& txid, const CTransactionRef& tx)
119 : : {
120 [ + - ]: 1660 : return WriteIC(std::make_pair(DBKeys::WTX_VARIANT, std::make_pair(txid, tx->GetWitnessHash())), TX_WITH_WITNESS(tx));
121 : : }
122 : :
123 : 1 : bool WalletBatch::WriteTxMetadata(const CWalletTx& wtx)
124 : : {
125 [ + - ]: 2 : return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
126 : : }
127 : :
128 : 0 : bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
129 : : {
130 [ # # ]: 0 : return WriteIC(std::make_pair(DBKeys::KEYMETA, pubkey), meta, overwrite);
131 : : }
132 : :
133 : 0 : bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
134 : : {
135 [ # # ]: 0 : if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
136 : : return false;
137 : : }
138 : :
139 : : // hash pubkey/privkey to accelerate wallet load
140 : 0 : const auto keypair_hash = Hash(vchPubKey, vchPrivKey);
141 : :
142 [ # # # # ]: 0 : return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, keypair_hash), false);
143 : : }
144 : :
145 : 0 : bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
146 : : const std::vector<unsigned char>& vchCryptedSecret,
147 : : const CKeyMetadata &keyMeta)
148 : : {
149 [ # # ]: 0 : if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
150 : : return false;
151 : : }
152 : :
153 : : // Compute a checksum of the encrypted key
154 : 0 : uint256 checksum = Hash(vchCryptedSecret);
155 : :
156 : 0 : const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
157 [ # # # # : 0 : if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
# # ]
158 : : // It may already exist, so try writing just the checksum
159 : 0 : std::vector<unsigned char> val;
160 [ # # # # ]: 0 : if (!m_batch->Read(key, val)) {
161 : : return false;
162 : : }
163 [ # # # # : 0 : if (!WriteIC(key, std::make_pair(val, checksum), true)) {
# # ]
164 : : return false;
165 : : }
166 : 0 : }
167 [ # # # # ]: 0 : EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
168 : 0 : return true;
169 : 0 : }
170 : :
171 : 16 : bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
172 : : {
173 [ - + + - ]: 48 : return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
174 : : }
175 : :
176 : 0 : bool WalletBatch::EraseMasterKey(unsigned int id)
177 : : {
178 [ # # # # ]: 0 : return EraseIC(std::make_pair(DBKeys::MASTER_KEY, id));
179 : : }
180 : :
181 : 0 : bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
182 : : {
183 [ # # # # ]: 0 : if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
184 : : return false;
185 : : }
186 [ # # ]: 0 : return WriteIC(std::make_pair(DBKeys::WATCHS, dest), uint8_t{'1'});
187 : : }
188 : :
189 : 47 : bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
190 : : {
191 [ + - ]: 47 : WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
192 : 94 : return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
193 : : }
194 : :
195 : 39 : bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
196 : : {
197 [ + - + - ]: 39 : if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
198 : 39 : return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
199 : : }
200 : :
201 : 0 : bool WalletBatch::IsEncrypted()
202 : : {
203 : 0 : DataStream prefix;
204 [ # # ]: 0 : prefix << DBKeys::MASTER_KEY;
205 [ # # # # : 0 : if (auto cursor = m_batch->GetNewPrefixCursor(prefix)) {
# # ]
206 : 0 : DataStream k, v;
207 [ # # # # ]: 0 : if (cursor->Next(k, v) == DatabaseCursor::Status::MORE) return true;
208 : 0 : }
209 : 0 : return false;
210 : 0 : }
211 : :
212 : 830 : bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
213 : : {
214 : 830 : return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
215 : : }
216 : :
217 : 393 : bool WalletBatch::WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal)
218 : : {
219 [ + + ]: 393 : std::string key = internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK;
220 [ - + + - ]: 1179 : return WriteIC(make_pair(key, type), id);
221 : 393 : }
222 : :
223 : 0 : bool WalletBatch::EraseActiveScriptPubKeyMan(uint8_t type, bool internal)
224 : : {
225 [ # # ]: 0 : const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
226 [ # # # # ]: 0 : return EraseIC(make_pair(key, type));
227 : 0 : }
228 : :
229 : 393 : bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
230 : : {
231 : : // hash pubkey/privkey to accelerate wallet load
232 : 393 : const auto keypair_hash = Hash(pubkey, privkey);
233 : :
234 [ + - + - ]: 786 : return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(privkey, keypair_hash), false);
235 : : }
236 : :
237 : 120 : bool WalletBatch::WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret)
238 : : {
239 : 120 : const auto descriptor_key{std::make_pair(desc_id, pubkey)};
240 : 120 : const auto plaintext_key{std::make_pair(DBKeys::WALLETDESCRIPTORKEY, descriptor_key)};
241 [ + - ]: 120 : const auto encrypted_key{std::make_pair(DBKeys::WALLETDESCRIPTORCKEY, descriptor_key)};
242 : :
243 : : // Keep the write and erase atomic even when the caller has not started a transaction
244 [ + - ]: 120 : const bool own_txn{!HasActiveTxn()};
245 [ + + + - : 120 : if (own_txn && !TxnBegin()) return false;
+ - ]
246 [ + - + - : 354 : if (!WriteIC(encrypted_key, secret, /*fOverwrite=*/false) || !EraseIC(plaintext_key) || (own_txn && !TxnCommit())) {
+ + + - +
+ ]
247 [ + + + - ]: 5 : if (own_txn) TxnAbort();
248 : 5 : return false;
249 : : }
250 : : return true;
251 : 120 : }
252 : :
253 : 14117 : bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor)
254 : : {
255 [ + - ]: 28234 : return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
256 : : }
257 : :
258 : 1000 : bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
259 : : {
260 : 1000 : std::vector<unsigned char> ser_xpub;
261 [ + - ]: 1000 : VectorWriter{ser_xpub, 0, xpub};
262 [ + - + - ]: 2000 : return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
263 : 1000 : }
264 : :
265 : 398 : bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
266 : : {
267 : 398 : std::vector<unsigned char> ser_xpub;
268 [ + - ]: 398 : VectorWriter{ser_xpub, 0, xpub};
269 [ + - + - ]: 796 : return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
270 : 398 : }
271 : :
272 : 396 : bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
273 : : {
274 : 396 : std::vector<unsigned char> ser_xpub;
275 [ + - ]: 396 : VectorWriter{ser_xpub, 0, xpub};
276 [ + - + - ]: 792 : return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub);
277 : 396 : }
278 : :
279 : 303294 : bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache)
280 : : {
281 [ + + + - ]: 303692 : for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) {
282 [ - + + - ]: 398 : if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) {
283 : 0 : return false;
284 : : }
285 : 0 : }
286 [ + + ]: 304294 : for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) {
287 [ + + + - ]: 2000 : for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
288 [ - + + - ]: 1000 : if (!WriteDescriptorDerivedCache(derived_xpub_pair.second, desc_id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
289 : 0 : return false;
290 : : }
291 : : }
292 : 0 : }
293 [ + + + - ]: 303690 : for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) {
294 [ - + + - ]: 396 : if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) {
295 : 0 : return false;
296 : : }
297 : 0 : }
298 : 303294 : return true;
299 : : }
300 : :
301 : 0 : bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
302 : : {
303 [ # # ]: 0 : return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
304 : : }
305 : :
306 : 0 : bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
307 : : {
308 [ # # ]: 0 : return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
309 : : }
310 : :
311 : 0 : bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
312 : : {
313 : 0 : LOCK(pwallet->cs_wallet);
314 : 0 : try {
315 [ # # ]: 0 : CPubKey vchPubKey;
316 [ # # ]: 0 : ssKey >> vchPubKey;
317 [ # # ]: 0 : if (!vchPubKey.IsValid())
318 : : {
319 [ # # # # ]: 0 : strErr = "Error reading wallet database: CPubKey corrupt";
320 : : return false;
321 : : }
322 : 0 : CKey key;
323 : 0 : CPrivKey pkey;
324 : 0 : uint256 hash;
325 : :
326 [ # # ]: 0 : ssValue >> pkey;
327 : :
328 : : // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
329 : : // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
330 : : // using EC operations as a checksum.
331 : : // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
332 : : // remaining backwards-compatible.
333 : 0 : try
334 : : {
335 [ # # ]: 0 : ssValue >> hash;
336 : : }
337 [ - - ]: 0 : catch (const std::ios_base::failure&) {}
338 : :
339 : 0 : bool fSkipCheck = false;
340 : :
341 [ # # ]: 0 : if (!hash.IsNull())
342 : : {
343 : : // hash pubkey/privkey to accelerate wallet load
344 [ # # ]: 0 : const auto keypair_hash = Hash(vchPubKey, pkey);
345 : :
346 [ # # ]: 0 : if (keypair_hash != hash)
347 : : {
348 [ # # ]: 0 : strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
349 : : return false;
350 : : }
351 : :
352 : : fSkipCheck = true;
353 : : }
354 : :
355 [ # # # # ]: 0 : if (!key.Load(pkey, vchPubKey, fSkipCheck))
356 : : {
357 [ # # ]: 0 : strErr = "Error reading wallet database: CPrivKey corrupt";
358 : : return false;
359 : : }
360 [ # # # # : 0 : if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadKey(key, vchPubKey))
# # ]
361 : : {
362 [ # # ]: 0 : strErr = "Error reading wallet database: LegacyDataSPKM::LoadKey failed";
363 : : return false;
364 : : }
365 [ # # ]: 0 : } catch (const std::exception& e) {
366 [ - - ]: 0 : if (strErr.empty()) {
367 [ - - ]: 0 : strErr = e.what();
368 : : }
369 : 0 : return false;
370 : 0 : }
371 : 0 : return true;
372 : 0 : }
373 : :
374 : 0 : bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
375 : : {
376 : 0 : LOCK(pwallet->cs_wallet);
377 : 0 : try {
378 [ # # ]: 0 : CPubKey vchPubKey;
379 [ # # ]: 0 : ssKey >> vchPubKey;
380 [ # # ]: 0 : if (!vchPubKey.IsValid())
381 : : {
382 [ # # # # ]: 0 : strErr = "Error reading wallet database: CPubKey corrupt";
383 : : return false;
384 : : }
385 : 0 : std::vector<unsigned char> vchPrivKey;
386 [ # # ]: 0 : ssValue >> vchPrivKey;
387 : :
388 : : // Get the checksum and check it
389 : 0 : bool checksum_valid = false;
390 [ # # # # ]: 0 : if (!ssValue.empty()) {
391 : 0 : uint256 checksum;
392 [ # # ]: 0 : ssValue >> checksum;
393 [ # # ]: 0 : if (!(checksum_valid = Hash(vchPrivKey) == checksum)) {
394 [ # # ]: 0 : strErr = "Error reading wallet database: Encrypted key corrupt";
395 : : return false;
396 : : }
397 : : }
398 : :
399 [ # # # # : 0 : if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
# # ]
400 : : {
401 [ # # ]: 0 : strErr = "Error reading wallet database: LegacyDataSPKM::LoadCryptedKey failed";
402 : : return false;
403 : : }
404 [ # # ]: 0 : } catch (const std::exception& e) {
405 [ - - ]: 0 : if (strErr.empty()) {
406 [ - - ]: 0 : strErr = e.what();
407 : : }
408 : 0 : return false;
409 : 0 : }
410 : 0 : return true;
411 : 0 : }
412 : :
413 : 0 : bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
414 : : {
415 : 0 : LOCK(pwallet->cs_wallet);
416 : 0 : try {
417 : : // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans.
418 : 0 : unsigned int nID;
419 [ # # ]: 0 : ssKey >> nID;
420 [ # # ]: 0 : CMasterKey kMasterKey;
421 [ # # ]: 0 : ssValue >> kMasterKey;
422 [ # # ]: 0 : if(pwallet->mapMasterKeys.contains(nID))
423 : : {
424 [ # # ]: 0 : strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
425 : 0 : return false;
426 : : }
427 [ # # # # ]: 0 : pwallet->mapMasterKeys[nID] = kMasterKey;
428 [ # # ]: 0 : if (pwallet->nMasterKeyMaxID < nID)
429 : 0 : pwallet->nMasterKeyMaxID = nID;
430 : :
431 [ # # ]: 0 : } catch (const std::exception& e) {
432 [ - - ]: 0 : if (strErr.empty()) {
433 [ - - ]: 0 : strErr = e.what();
434 : : }
435 : 0 : return false;
436 : 0 : }
437 : 0 : return true;
438 : 0 : }
439 : :
440 : 0 : bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr)
441 : : {
442 : 0 : LOCK(pwallet->cs_wallet);
443 : 0 : try {
444 : 0 : CHDChain chain;
445 [ # # ]: 0 : ssValue >> chain;
446 [ # # # # ]: 0 : pwallet->GetOrCreateLegacyDataSPKM()->LoadHDChain(chain);
447 [ - - ]: 0 : } catch (const std::exception& e) {
448 [ - - ]: 0 : if (strErr.empty()) {
449 [ - - ]: 0 : strErr = e.what();
450 : : }
451 : 0 : return false;
452 : 0 : }
453 : : return true;
454 : 0 : }
455 : :
456 : 10 : static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
457 : : {
458 : 10 : AssertLockHeld(pwallet->cs_wallet);
459 : 10 : uint64_t flags;
460 [ + + ]: 10 : if (batch.Read(DBKeys::FLAGS, flags)) {
461 [ - + ]: 3 : if (!pwallet->LoadWalletFlags(flags)) {
462 : 0 : pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
463 : 0 : return DBErrors::TOO_NEW;
464 : : }
465 : : // All wallets must be descriptor wallets unless opened with a bdb_ro db
466 : : // bdb_ro is only used for legacy to descriptor migration.
467 [ + - + - : 6 : if (pwallet->GetDatabase().Format() != "bdb_ro" && !pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
+ - - + ]
468 : 0 : return DBErrors::LEGACY_WALLET;
469 : : }
470 : : }
471 : : return DBErrors::LOAD_OK;
472 : : }
473 : :
474 : : struct LoadResult
475 : : {
476 : : DBErrors m_result{DBErrors::LOAD_OK};
477 : : int m_records{0};
478 : : };
479 : :
480 : : using LoadFunc = std::function<DBErrors(CWallet* pwallet, DataStream& key, DataStream& value, std::string& err)>;
481 : 274 : static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, DataStream& prefix, LoadFunc load_func)
482 : : {
483 : 274 : LoadResult result;
484 : 274 : DataStream ssKey;
485 : 274 : DataStream ssValue{};
486 : :
487 [ - + + - ]: 274 : Assume(!prefix.empty());
488 [ + - ]: 274 : std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
489 [ - + ]: 274 : if (!cursor) {
490 [ # # ]: 0 : pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", key);
491 : 0 : result.m_result = DBErrors::CORRUPT;
492 : 0 : return result;
493 : : }
494 : :
495 : 590 : while (true) {
496 [ + - ]: 432 : DatabaseCursor::Status status = cursor->Next(ssKey, ssValue);
497 [ + + ]: 432 : if (status == DatabaseCursor::Status::DONE) {
498 : : break;
499 [ - + ]: 158 : } else if (status == DatabaseCursor::Status::FAIL) {
500 [ # # ]: 0 : pwallet->WalletLogPrintf("Error reading next '%s' record for wallet database\n", key);
501 : 0 : result.m_result = DBErrors::CORRUPT;
502 : 0 : return result;
503 : : }
504 [ + - ]: 158 : std::string type;
505 [ + - ]: 158 : ssKey >> type;
506 [ - + ]: 158 : assert(type == key);
507 [ + - ]: 158 : std::string error;
508 [ + - ]: 158 : DBErrors record_res = load_func(pwallet, ssKey, ssValue, error);
509 [ + + ]: 158 : if (record_res != DBErrors::LOAD_OK) {
510 [ + - ]: 5 : pwallet->WalletLogPrintf("%s\n", error);
511 : : }
512 [ + + ]: 158 : result.m_result = std::max(result.m_result, record_res);
513 : 158 : ++result.m_records;
514 : 158 : }
515 : 274 : return result;
516 : 274 : }
517 : :
518 : 154 : static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, LoadFunc load_func)
519 : : {
520 : 154 : DataStream prefix;
521 [ + - ]: 154 : prefix << key;
522 [ + - + - ]: 308 : return LoadRecords(pwallet, batch, key, prefix, load_func);
523 : 154 : }
524 : :
525 : 0 : bool HasLegacyRecords(CWallet& wallet)
526 : : {
527 : 0 : const auto& batch = wallet.GetDatabase().MakeBatch();
528 [ # # ]: 0 : return HasLegacyRecords(wallet, *batch);
529 : : }
530 : :
531 : 3 : bool HasLegacyRecords(CWallet& wallet, DatabaseBatch& batch)
532 : : {
533 [ + + + - ]: 33 : for (const auto& type : DBKeys::LEGACY_TYPES) {
534 : 30 : DataStream key;
535 : 30 : DataStream value{};
536 : 30 : DataStream prefix;
537 : :
538 [ + - ]: 30 : prefix << type;
539 [ - + + - ]: 30 : std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
540 [ - + ]: 30 : if (!cursor) {
541 : : // Could only happen on a closed db, which means there is an error in the code flow.
542 [ # # # # ]: 0 : throw std::runtime_error(strprintf("Error getting database cursor for '%s' records", type));
543 : : }
544 : :
545 [ + - ]: 30 : DatabaseCursor::Status status = cursor->Next(key, value);
546 [ - + ]: 30 : if (status != DatabaseCursor::Status::DONE) {
547 : 0 : return true;
548 : : }
549 : 30 : }
550 : : return false;
551 : : }
552 : :
553 : 10 : static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
554 : : {
555 : 10 : AssertLockHeld(pwallet->cs_wallet);
556 : 10 : DBErrors result = DBErrors::LOAD_OK;
557 : :
558 : : // Make sure descriptor wallets don't have any legacy records
559 [ + + ]: 10 : if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
560 [ - + ]: 3 : if (HasLegacyRecords(*pwallet, batch)) {
561 : 0 : pwallet->WalletLogPrintf("Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent.\n", pwallet->GetName());
562 : 0 : return DBErrors::UNEXPECTED_LEGACY_ENTRY;
563 : : }
564 : :
565 : : return DBErrors::LOAD_OK;
566 : : }
567 : :
568 : : // Load HD Chain
569 : : // Note: There should only be one HDCHAIN record with no data following the type
570 [ + - ]: 7 : LoadResult hd_chain_res = LoadRecords(pwallet, batch, DBKeys::HDCHAIN,
571 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
572 [ # # ]: 0 : return LoadHDChain(pwallet, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT;
573 : : });
574 [ + - ]: 7 : result = std::max(result, hd_chain_res.m_result);
575 : :
576 : : // Load unencrypted keys
577 [ + - ]: 7 : LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::KEY,
578 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
579 [ # # ]: 0 : return LoadKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT;
580 : : });
581 [ + - ]: 7 : result = std::max(result, key_res.m_result);
582 : :
583 : : // Load encrypted keys
584 [ + - ]: 7 : LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::CRYPTED_KEY,
585 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
586 [ # # ]: 0 : return LoadCryptedKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT;
587 : : });
588 [ + - ]: 7 : result = std::max(result, ckey_res.m_result);
589 : :
590 : : // Load scripts
591 [ + - ]: 7 : LoadResult script_res = LoadRecords(pwallet, batch, DBKeys::CSCRIPT,
592 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
593 : 0 : uint160 hash;
594 : 0 : key >> hash;
595 : 0 : CScript script;
596 [ # # ]: 0 : value >> script;
597 [ # # # # : 0 : if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCScript(script))
# # ]
598 : : {
599 [ # # ]: 0 : strErr = "Error reading wallet database: LegacyDataSPKM::LoadCScript failed";
600 : : return DBErrors::NONCRITICAL_ERROR;
601 : : }
602 : : return DBErrors::LOAD_OK;
603 : 0 : });
604 [ + - ]: 7 : result = std::max(result, script_res.m_result);
605 : :
606 : : // Load keymeta
607 [ + - ]: 7 : std::map<uint160, CHDChain> hd_chains;
608 : 7 : LoadResult keymeta_res = LoadRecords(pwallet, batch, DBKeys::KEYMETA,
609 [ + - ]: 7 : [&hd_chains] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
610 : 0 : CPubKey vchPubKey;
611 : 0 : key >> vchPubKey;
612 : 0 : CKeyMetadata keyMeta;
613 [ # # ]: 0 : value >> keyMeta;
614 [ # # # # : 0 : pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
# # ]
615 : :
616 : : // Extract some CHDChain info from this metadata if it has any
617 [ # # # # : 0 : if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
# # ]
618 : : // Get the path from the key origin or from the path string
619 : : // Not applicable when path is "s" or "m" as those indicate a seed
620 : : // See https://github.com/bitcoin/bitcoin/pull/12924
621 : 0 : bool internal = false;
622 : 0 : uint32_t index = 0;
623 [ # # # # ]: 0 : if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") {
624 : 0 : std::vector<uint32_t> path;
625 [ # # ]: 0 : if (keyMeta.has_key_origin) {
626 : : // We have a key origin, so pull it from its path vector
627 [ # # ]: 0 : path = keyMeta.key_origin.path;
628 : : } else {
629 : : // No key origin, have to parse the string
630 [ # # # # ]: 0 : if (!ParseHDKeypath(keyMeta.hdKeypath, path)) {
631 [ # # ]: 0 : strErr = "Error reading wallet database: keymeta with invalid HD keypath";
632 : : return DBErrors::NONCRITICAL_ERROR;
633 : : }
634 : : }
635 : :
636 : : // Extract the index and internal from the path
637 : : // Path string is m/0'/k'/i'
638 : : // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit
639 : : // k == 0 for external, 1 for internal. i is the index
640 [ # # # # ]: 0 : if (path.size() != 3) {
641 [ # # ]: 0 : strErr = "Error reading wallet database: keymeta found with unexpected path";
642 : : return DBErrors::NONCRITICAL_ERROR;
643 : : }
644 [ # # ]: 0 : if (path[0] != BIP32_HARDENED_FLAG) {
645 [ # # ]: 0 : strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]);
646 : 0 : return DBErrors::NONCRITICAL_ERROR;
647 : : }
648 [ # # # # ]: 0 : if (path[1] != BIP32_HARDENED_FLAG && path[1] != (1 | BIP32_HARDENED_FLAG)) {
649 [ # # ]: 0 : strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]);
650 : 0 : return DBErrors::NONCRITICAL_ERROR;
651 : : }
652 [ # # ]: 0 : if ((path[2] & BIP32_HARDENED_FLAG) == 0) {
653 [ # # ]: 0 : strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]);
654 : 0 : return DBErrors::NONCRITICAL_ERROR;
655 : : }
656 : 0 : internal = path[1] == (1 | BIP32_HARDENED_FLAG);
657 : 0 : index = path[2] & ~BIP32_HARDENED_FLAG;
658 : 0 : }
659 : :
660 : : // Insert a new CHDChain, or get the one that already exists
661 [ # # # # ]: 0 : auto [ins, inserted] = hd_chains.emplace(keyMeta.hd_seed_id, CHDChain());
662 [ # # ]: 0 : CHDChain& chain = ins->second;
663 [ # # ]: 0 : if (inserted) {
664 : : // For new chains, we want to default to VERSION_HD_BASE until we see an internal
665 : 0 : chain.nVersion = CHDChain::VERSION_HD_BASE;
666 : 0 : chain.seed_id = keyMeta.hd_seed_id;
667 : : }
668 [ # # ]: 0 : if (internal) {
669 : 0 : chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT;
670 [ # # ]: 0 : chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1);
671 : : } else {
672 [ # # ]: 0 : chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1);
673 : : }
674 : : }
675 : : return DBErrors::LOAD_OK;
676 : 0 : });
677 [ + - ]: 7 : result = std::max(result, keymeta_res.m_result);
678 : :
679 : : // Set inactive chains
680 [ - + ]: 7 : if (!hd_chains.empty()) {
681 [ # # ]: 0 : LegacyDataSPKM* legacy_spkm = pwallet->GetLegacyDataSPKM();
682 [ # # ]: 0 : if (legacy_spkm) {
683 [ # # # # ]: 0 : for (const auto& [hd_seed_id, chain] : hd_chains) {
684 [ # # ]: 0 : if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) {
685 [ # # ]: 0 : legacy_spkm->AddInactiveHDChain(chain);
686 : : }
687 : : }
688 : : } else {
689 [ # # ]: 0 : pwallet->WalletLogPrintf("Inactive HD chains found but no LegacyDataSPKM\n");
690 : 0 : result = DBErrors::CORRUPT;
691 : : }
692 : : }
693 : :
694 : : // Load watchonly scripts
695 [ + - ]: 7 : LoadResult watch_script_res = LoadRecords(pwallet, batch, DBKeys::WATCHS,
696 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
697 : 0 : CScript script;
698 [ # # ]: 0 : key >> script;
699 : 0 : uint8_t fYes;
700 [ # # ]: 0 : value >> fYes;
701 [ # # ]: 0 : if (fYes == '1') {
702 [ # # # # ]: 0 : pwallet->GetOrCreateLegacyDataSPKM()->LoadWatchOnly(script);
703 : : }
704 : 0 : return DBErrors::LOAD_OK;
705 : 0 : });
706 [ + - ]: 7 : result = std::max(result, watch_script_res.m_result);
707 : :
708 : : // Load watchonly meta
709 [ + - ]: 7 : LoadResult watch_meta_res = LoadRecords(pwallet, batch, DBKeys::WATCHMETA,
710 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
711 : 0 : CScript script;
712 [ # # ]: 0 : key >> script;
713 : 0 : CKeyMetadata keyMeta;
714 [ # # ]: 0 : value >> keyMeta;
715 [ # # # # : 0 : pwallet->GetOrCreateLegacyDataSPKM()->LoadScriptMetadata(CScriptID(script), keyMeta);
# # ]
716 : 0 : return DBErrors::LOAD_OK;
717 : 0 : });
718 [ + - ]: 7 : result = std::max(result, watch_meta_res.m_result);
719 : :
720 : : // Deal with old "wkey" and "defaultkey" records.
721 : : // These are not actually loaded, but we need to check for them
722 : :
723 : : // We don't want or need the default key, but if there is one set,
724 : : // we want to make sure that it is valid so that we can detect corruption
725 : : // Note: There should only be one DEFAULTKEY with nothing trailing the type
726 [ + - ]: 7 : LoadResult default_key_res = LoadRecords(pwallet, batch, DBKeys::DEFAULTKEY,
727 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
728 [ # # ]: 0 : CPubKey default_pubkey;
729 : 0 : try {
730 [ # # ]: 0 : value >> default_pubkey;
731 [ - - ]: 0 : } catch (const std::exception& e) {
732 [ - - ]: 0 : err = e.what();
733 : 0 : return DBErrors::CORRUPT;
734 : 0 : }
735 [ # # ]: 0 : if (!default_pubkey.IsValid()) {
736 : 0 : err = "Error reading wallet database: Default Key corrupt";
737 : 0 : return DBErrors::CORRUPT;
738 : : }
739 : : return DBErrors::LOAD_OK;
740 : : });
741 [ + - ]: 7 : result = std::max(result, default_key_res.m_result);
742 : :
743 : : // "wkey" records are unsupported, if we see any, throw an error
744 [ + - ]: 7 : LoadResult wkey_res = LoadRecords(pwallet, batch, DBKeys::OLD_KEY,
745 [ + - ]: 7 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
746 : 0 : err = "Found unsupported 'wkey' record, try loading with version 0.18";
747 : 0 : return DBErrors::LOAD_FAIL;
748 : : });
749 [ + - ]: 7 : result = std::max(result, wkey_res.m_result);
750 : :
751 [ + - ]: 7 : if (result <= DBErrors::NONCRITICAL_ERROR) {
752 : : // Only do logging and time first key update if there were no critical errors
753 : 7 : pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n",
754 [ + - ]: 7 : key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records);
755 : : }
756 : :
757 : 7 : return result;
758 : 7 : }
759 : :
760 : : template<typename... Args>
761 : 120 : static DataStream PrefixStream(const Args&... args)
762 : : {
763 : 120 : DataStream prefix;
764 [ + - ]: 120 : SerializeMany(prefix, args...);
765 : 120 : return prefix;
766 : 0 : }
767 : :
768 : 10 : static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
769 : : {
770 : 10 : AssertLockHeld(pwallet->cs_wallet);
771 : :
772 : : // Load descriptor record
773 : 10 : int num_keys = 0;
774 : 10 : int num_ckeys= 0;
775 [ + - ]: 10 : LoadResult desc_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTOR,
776 : 10 : [&batch, &num_keys, &num_ckeys, &last_client] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
777 : 31 : DBErrors result = DBErrors::LOAD_OK;
778 : :
779 : 31 : uint256 id;
780 : 31 : key >> id;
781 : 31 : std::optional<WalletDescriptor> desc;
782 : 31 : try {
783 [ + + ]: 31 : desc.emplace(WalletDescriptor::FromStream(deserialize, value));
784 [ - + ]: 1 : } catch (const std::ios_base::failure& e) {
785 [ + - ]: 1 : strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName());
786 : 1 : strErr += (last_client > VERSION_LATEST) ? "The wallet might have been created on a newer version. " :
787 [ + - ]: 1 : "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. ";
788 [ + - ]: 1 : strErr += "Please try running the latest software version";
789 : : // Also include error details
790 [ + - ]: 1 : strErr = strprintf("%s\nDetails: %s", strErr, e.what());
791 : 1 : return DBErrors::UNKNOWN_DESCRIPTOR;
792 : 1 : }
793 : :
794 : 30 : DescriptorCache cache;
795 : :
796 : : // Get key cache for this descriptor
797 [ + - ]: 30 : DataStream prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCACHE, id);
798 : 30 : LoadResult key_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCACHE, prefix,
799 [ + - ]: 30 : [&id, &cache] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
800 : 25 : bool parent = true;
801 : 25 : uint256 desc_id;
802 : 25 : uint32_t key_exp_index;
803 : 25 : uint32_t der_index;
804 : 25 : key >> desc_id;
805 [ + - ]: 25 : assert(desc_id == id);
806 : 25 : key >> key_exp_index;
807 : :
808 : : // if the der_index exists, it's a derived xpub
809 : 25 : try
810 : : {
811 [ - + ]: 25 : key >> der_index;
812 : : parent = false;
813 : : }
814 : 25 : catch (...) {}
815 : :
816 : : // The xpub is stored as a length-prefixed byte vector
817 [ + + ]: 25 : if (ReadCompactSize(value) != BIP32_EXTKEY_SIZE) {
818 : 1 : err = "Error reading wallet database: descriptor cache xpub has invalid size";
819 : 1 : return DBErrors::CORRUPT;
820 : : }
821 [ + - ]: 24 : CExtPubKey xpub;
822 [ + - ]: 24 : value >> xpub;
823 [ + - ]: 24 : if (parent) {
824 [ + - ]: 24 : cache.CacheParentExtPubKey(key_exp_index, xpub);
825 : : } else {
826 [ # # ]: 0 : cache.CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
827 : : }
828 : 24 : return DBErrors::LOAD_OK;
829 : 24 : });
830 [ + + ]: 30 : result = std::max(result, key_cache_res.m_result);
831 : :
832 : : // Get last hardened cache for this descriptor
833 [ + - ]: 60 : prefix = PrefixStream(DBKeys::WALLETDESCRIPTORLHCACHE, id);
834 : 30 : LoadResult lh_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORLHCACHE, prefix,
835 [ + - ]: 30 : [&id, &cache] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
836 : 25 : uint256 desc_id;
837 : 25 : uint32_t key_exp_index;
838 : 25 : key >> desc_id;
839 [ + - ]: 25 : assert(desc_id == id);
840 : 25 : key >> key_exp_index;
841 : :
842 : : // The xpub is stored as a length-prefixed byte vector
843 [ + + ]: 25 : if (ReadCompactSize(value) != BIP32_EXTKEY_SIZE) {
844 : 1 : err = "Error reading wallet database: descriptor last hardened cache xpub has invalid size";
845 : 1 : return DBErrors::CORRUPT;
846 : : }
847 [ + - ]: 24 : CExtPubKey xpub;
848 [ + - ]: 24 : value >> xpub;
849 [ + - ]: 24 : cache.CacheLastHardenedExtPubKey(key_exp_index, xpub);
850 : 24 : return DBErrors::LOAD_OK;
851 : 24 : });
852 [ + + ]: 30 : result = std::max(result, lh_cache_res.m_result);
853 : :
854 : : // Set the cache to the WalletDescriptor
855 [ + - ]: 30 : desc->cache = cache;
856 : :
857 : : // Get unencrypted keys
858 [ + - ]: 30 : KeyMap keys;
859 [ + - ]: 60 : prefix = PrefixStream(DBKeys::WALLETDESCRIPTORKEY, id);
860 : 30 : LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORKEY, prefix,
861 [ + - ]: 30 : [&id, &keys] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
862 : 27 : uint256 desc_id;
863 : 27 : CPubKey pubkey;
864 : 27 : key >> desc_id;
865 [ + - ]: 27 : assert(desc_id == id);
866 : 27 : key >> pubkey;
867 [ - + ]: 27 : if (!pubkey.IsValid())
868 : : {
869 : 0 : strErr = "Error reading wallet database: descriptor unencrypted key CPubKey corrupt";
870 : 0 : return DBErrors::CORRUPT;
871 : : }
872 : 27 : CKey privkey;
873 : 27 : CPrivKey pkey;
874 : 27 : uint256 hash;
875 : :
876 [ + - ]: 27 : value >> pkey;
877 [ + - ]: 27 : value >> hash;
878 : :
879 : : // hash pubkey/privkey to accelerate wallet load
880 [ + - ]: 27 : const auto keypair_hash = Hash(pubkey, pkey);
881 : :
882 [ - + ]: 27 : if (keypair_hash != hash)
883 : : {
884 [ # # ]: 0 : strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt";
885 : : return DBErrors::CORRUPT;
886 : : }
887 : :
888 [ + - - + ]: 27 : if (!privkey.Load(pkey, pubkey, true))
889 : : {
890 [ # # ]: 0 : strErr = "Error reading wallet database: descriptor unencrypted key CPrivKey corrupt";
891 : : return DBErrors::CORRUPT;
892 : : }
893 [ + - + - : 27 : keys[pubkey.GetID()] = privkey;
+ - ]
894 : : return DBErrors::LOAD_OK;
895 : 27 : });
896 [ + - ]: 30 : result = std::max(result, key_res.m_result);
897 : 30 : num_keys = key_res.m_records;
898 : :
899 : : // Get encrypted keys
900 [ + - ]: 30 : CryptedKeyMap ckeys;
901 [ + - ]: 60 : prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCKEY, id);
902 : 30 : LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCKEY, prefix,
903 [ + - ]: 30 : [&id, &ckeys] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
904 : 0 : uint256 desc_id;
905 : 0 : CPubKey pubkey;
906 : 0 : key >> desc_id;
907 [ # # ]: 0 : assert(desc_id == id);
908 : 0 : key >> pubkey;
909 [ # # ]: 0 : if (!pubkey.IsValid())
910 : : {
911 : 0 : err = "Error reading wallet database: descriptor encrypted key CPubKey corrupt";
912 : 0 : return DBErrors::CORRUPT;
913 : : }
914 : 0 : std::vector<unsigned char> privkey;
915 [ # # ]: 0 : value >> privkey;
916 : :
917 [ # # # # : 0 : ckeys[pubkey.GetID()] = std::make_pair(pubkey, privkey);
# # ]
918 : 0 : return DBErrors::LOAD_OK;
919 : 0 : });
920 [ + - ]: 30 : result = std::max(result, ckey_res.m_result);
921 : 30 : num_ckeys = ckey_res.m_records;
922 : :
923 : 30 : try {
924 [ + - ]: 30 : pwallet->LoadDescriptorScriptPubKeyMan(id, *desc, keys, ckeys);
925 [ - - ]: 0 : } catch (std::runtime_error& e) {
926 [ - - ]: 0 : strErr = e.what();
927 : 0 : return DBErrors::CORRUPT;
928 : 0 : }
929 : :
930 : 30 : return result;
931 : 61 : });
932 : :
933 [ + + ]: 10 : if (desc_res.m_result <= DBErrors::NONCRITICAL_ERROR) {
934 : : // Only log if there are no critical errors
935 : 7 : pwallet->WalletLogPrintf("Descriptors: %u, Descriptor Keys: %u plaintext, %u encrypted, %u total.\n",
936 : 7 : desc_res.m_records, num_keys, num_ckeys, num_keys + num_ckeys);
937 : : }
938 : :
939 : 10 : return desc_res.m_result;
940 : : }
941 : :
942 : 9 : static DBErrors LoadAddressBookRecords(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
943 : : {
944 : 9 : AssertLockHeld(pwallet->cs_wallet);
945 : 9 : DBErrors result = DBErrors::LOAD_OK;
946 : :
947 : : // Load name record
948 [ + - ]: 9 : LoadResult name_res = LoadRecords(pwallet, batch, DBKeys::NAME,
949 [ + - ]: 9 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
950 [ + - ]: 9 : std::string strAddress;
951 [ + - ]: 9 : key >> strAddress;
952 [ + - ]: 9 : std::string label;
953 [ + - ]: 9 : value >> label;
954 [ + - + - : 18 : pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
- + ]
955 : 9 : return DBErrors::LOAD_OK;
956 : 9 : });
957 [ + - ]: 9 : result = std::max(result, name_res.m_result);
958 : :
959 : : // Load purpose record
960 [ + - ]: 9 : LoadResult purpose_res = LoadRecords(pwallet, batch, DBKeys::PURPOSE,
961 [ + - ]: 9 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
962 [ + - ]: 9 : std::string strAddress;
963 [ + - ]: 9 : key >> strAddress;
964 [ + - ]: 9 : std::string purpose_str;
965 [ + - ]: 9 : value >> purpose_str;
966 [ - + ]: 9 : std::optional<AddressPurpose> purpose{PurposeFromString(purpose_str)};
967 [ - + ]: 9 : if (!purpose) {
968 [ # # ]: 0 : pwallet->WalletLogPrintf("Warning: nonstandard purpose string '%s' for address '%s'\n", purpose_str, strAddress);
969 : : }
970 [ + - + - ]: 9 : pwallet->m_address_book[DecodeDestination(strAddress)].purpose = purpose;
971 : 9 : return DBErrors::LOAD_OK;
972 : 9 : });
973 [ + - ]: 9 : result = std::max(result, purpose_res.m_result);
974 : :
975 : : // Load destination data record
976 [ + - ]: 9 : LoadResult dest_res = LoadRecords(pwallet, batch, DBKeys::DESTDATA,
977 [ + - ]: 9 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
978 [ + - ]: 5 : std::string strAddress, strKey, strValue;
979 [ + - ]: 5 : key >> strAddress;
980 [ + - ]: 5 : key >> strKey;
981 [ + - ]: 5 : value >> strValue;
982 [ + - ]: 5 : const CTxDestination& dest{DecodeDestination(strAddress)};
983 [ + + ]: 5 : if (strKey.compare("used") == 0) {
984 : : // Load "used" key indicating if an IsMine address has
985 : : // previously been spent from with avoid_reuse option enabled.
986 : : // The strValue is not used for anything currently, but could
987 : : // hold more information in the future. Current values are just
988 : : // "1" or "p" for present (which was written prior to
989 : : // f5ba424cd44619d9b9be88b8593d69a7ba96db26).
990 [ + - ]: 2 : pwallet->LoadAddressPreviouslySpent(dest);
991 [ - + + - ]: 3 : } else if (strKey.starts_with("rr")) {
992 : : // Load "rr##" keys where ## is a decimal number, and strValue
993 : : // is a serialized RecentRequestEntry object.
994 [ + - + - ]: 6 : pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue);
995 : : }
996 : 5 : return DBErrors::LOAD_OK;
997 : 5 : });
998 [ + - ]: 9 : result = std::max(result, dest_res.m_result);
999 : :
1000 : 9 : return result;
1001 : : }
1002 : :
1003 : 2 : static std::map<Wtxid, CTransactionRef> ReadWtxVariants(DatabaseBatch& batch, const Txid& txid)
1004 : : {
1005 [ + - ]: 2 : std::map<Wtxid, CTransactionRef> variants;
1006 : :
1007 : 2 : DataStream prefix;
1008 [ + - + - ]: 2 : prefix << DBKeys::WTX_VARIANT << txid;
1009 [ - + + - ]: 2 : std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
1010 [ - + ]: 2 : if (!cursor) {
1011 [ # # # # ]: 0 : throw std::runtime_error(strprintf("Error getting database cursor for '%s' records", DBKeys::WTX_VARIANT));
1012 : : }
1013 : :
1014 : 2 : DataStream key;
1015 : 2 : DataStream value;
1016 : 6 : while (true) {
1017 [ + - ]: 4 : DatabaseCursor::Status status = cursor->Next(key, value);
1018 [ + + ]: 4 : if (status == DatabaseCursor::Status::DONE) break;
1019 [ - + ]: 2 : if (status == DatabaseCursor::Status::FAIL) {
1020 [ # # # # ]: 0 : throw std::runtime_error(strprintf("Error reading '%s' record", DBKeys::WTX_VARIANT));
1021 : : }
1022 : 2 : CTransactionRef tx;
1023 [ + - ]: 2 : value >> TX_WITH_WITNESS(tx);
1024 [ + - ]: 2 : if (tx->GetHash() != txid) {
1025 [ # # # # ]: 0 : throw std::runtime_error(strprintf("Corrupted witness variant, tx hash differs"));
1026 : : }
1027 [ + - - + ]: 2 : if (!variants.emplace(tx->GetWitnessHash(), std::move(tx)).second) {
1028 [ # # # # ]: 0 : throw std::runtime_error(strprintf("Duplicate witness variant"));
1029 : : }
1030 : 2 : }
1031 : 2 : return variants;
1032 : 2 : }
1033 : :
1034 : 9 : static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1035 : : {
1036 : 9 : AssertLockHeld(pwallet->cs_wallet);
1037 : 9 : DBErrors result = DBErrors::LOAD_OK;
1038 : :
1039 : : // Load tx record
1040 : 9 : any_unordered = false;
1041 : 9 : LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX,
1042 [ + - ]: 9 : [&any_unordered, &batch] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1043 : 2 : DBErrors result = DBErrors::LOAD_OK;
1044 : 2 : Txid hash;
1045 : 2 : key >> hash;
1046 : 2 : try {
1047 [ + - + - ]: 2 : CWalletTx wtx{deserialize, value, ReadWtxVariants(batch, hash)};
1048 [ + - - + ]: 2 : if (wtx.GetHash() != hash) {
1049 [ # # ]: 0 : result = std::max(result, DBErrors::NEED_RESCAN);
1050 : : }
1051 : :
1052 [ - + ]: 2 : if (wtx.nOrderPos == -1) {
1053 : 0 : any_unordered = true;
1054 : : }
1055 : :
1056 [ + - - + ]: 2 : if (!pwallet->LoadToWallet(std::move(wtx))) {
1057 [ # # ]: 0 : err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";
1058 : 0 : return DBErrors::CORRUPT;
1059 : : }
1060 [ - - ]: 2 : } catch (const std::exception& e) {
1061 [ - - ]: 0 : err = strprintf("Error: Corrupt tx record found: %s" ,e.what());
1062 : 0 : return DBErrors::CORRUPT;
1063 : 0 : }
1064 : 2 : return result;
1065 : : });
1066 [ + - ]: 9 : result = std::max(result, tx_res.m_result);
1067 : :
1068 : : // Load locked utxo record
1069 [ + - ]: 9 : LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,
1070 [ + - ]: 9 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1071 : 0 : Txid hash;
1072 : 0 : uint32_t n;
1073 : 0 : key >> hash;
1074 : 0 : key >> n;
1075 : 0 : pwallet->LoadLockedCoin(COutPoint(hash, n), /*persistent=*/true);
1076 : 0 : return DBErrors::LOAD_OK;
1077 : : });
1078 [ + - ]: 9 : result = std::max(result, locked_utxo_res.m_result);
1079 : :
1080 : : // Load orderposnext record
1081 : : // Note: There should only be one ORDERPOSNEXT record with nothing trailing the type
1082 [ + - ]: 9 : LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,
1083 [ + - ]: 9 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1084 : 1 : try {
1085 [ + - ]: 2 : value >> pwallet->nOrderPosNext;
1086 [ - - ]: 0 : } catch (const std::exception& e) {
1087 [ - - ]: 0 : err = e.what();
1088 : 0 : return DBErrors::NONCRITICAL_ERROR;
1089 : 0 : }
1090 : : return DBErrors::LOAD_OK;
1091 : : });
1092 [ + - ]: 9 : result = std::max(result, order_pos_res.m_result);
1093 : :
1094 : : // After loading all tx records, abandon any coinbase that is no longer in the active chain.
1095 : : // This could happen during an external wallet load, or if the user replaced the chain data.
1096 [ + + ]: 11 : for (auto& [id, wtx] : pwallet->mapWallet) {
1097 [ - + ]: 2 : if (wtx.IsCoinBase() && wtx.isInactive()) {
1098 : 0 : pwallet->AbandonTransaction(wtx);
1099 : : }
1100 : : }
1101 : :
1102 : 9 : return result;
1103 : : }
1104 : :
1105 : 9 : static DBErrors LoadActiveSPKMs(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1106 : : {
1107 : 9 : AssertLockHeld(pwallet->cs_wallet);
1108 : 9 : DBErrors result = DBErrors::LOAD_OK;
1109 : :
1110 : : // Load spk records
1111 [ - + ]: 9 : std::set<std::pair<OutputType, bool>> seen_spks;
1112 [ - + - + : 63 : for (const auto& spk_key : {DBKeys::ACTIVEEXTERNALSPK, DBKeys::ACTIVEINTERNALSPK}) {
+ + - - ]
1113 : 18 : LoadResult spkm_res = LoadRecords(pwallet, batch, spk_key,
1114 [ + - ]: 18 : [&seen_spks, &spk_key] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
1115 : 24 : uint8_t output_type;
1116 : 24 : key >> output_type;
1117 : 24 : uint256 id;
1118 : 24 : value >> id;
1119 : :
1120 : 24 : bool internal = spk_key == DBKeys::ACTIVEINTERNALSPK;
1121 [ - + ]: 24 : auto [it, insert] = seen_spks.emplace(static_cast<OutputType>(output_type), internal);
1122 [ - + ]: 24 : if (!insert) {
1123 : 0 : strErr = "Multiple ScriptpubKeyMans specified for a single type";
1124 : 0 : return DBErrors::CORRUPT;
1125 : : }
1126 : 24 : pwallet->LoadActiveScriptPubKeyMan(id, static_cast<OutputType>(output_type), /*internal=*/internal);
1127 : 24 : return DBErrors::LOAD_OK;
1128 : : });
1129 [ + - ]: 36 : result = std::max(result, spkm_res.m_result);
1130 [ + + - - ]: 27 : }
1131 : 9 : return result;
1132 : 9 : }
1133 : :
1134 : 9 : static DBErrors LoadDecryptionKeys(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1135 : : {
1136 : 9 : AssertLockHeld(pwallet->cs_wallet);
1137 : :
1138 : : // Load decryption key (mkey) records
1139 [ + - ]: 9 : LoadResult mkey_res = LoadRecords(pwallet, batch, DBKeys::MASTER_KEY,
1140 [ + - ]: 9 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
1141 [ # # ]: 0 : if (!LoadEncryptionKey(pwallet, key, value, err)) {
1142 : 0 : return DBErrors::CORRUPT;
1143 : : }
1144 : : return DBErrors::LOAD_OK;
1145 : : });
1146 : 9 : return mkey_res.m_result;
1147 : : }
1148 : :
1149 : 10 : DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
1150 : : {
1151 : 10 : DBErrors result = DBErrors::LOAD_OK;
1152 : 10 : bool any_unordered = false;
1153 : :
1154 : 10 : LOCK(pwallet->cs_wallet);
1155 : :
1156 : : // Last client version to open this wallet
1157 : 10 : int last_client = VERSION_LATEST;
1158 [ + - ]: 10 : bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
1159 [ + + + - ]: 10 : if (has_last_client) pwallet->WalletLogPrintf("Last client version = %d\n", last_client);
1160 : :
1161 : 10 : std::optional<uint64_t> last_client_features;
1162 [ + - ]: 10 : if (last_client >= VERSION_LAST_CLIENT_FEATURES) {
1163 : : // Features of last client to open this wallet
1164 [ + - + + ]: 10 : if (uint64_t features; m_batch->Read(DBKeys::LAST_OPENED_FEATURES, features)) {
1165 : 5 : last_client_features = features;
1166 : : }
1167 : :
1168 : : // Features of last client to decrypt this wallet
1169 [ + - - + ]: 10 : if (uint64_t last_decrypted; m_batch->Read(DBKeys::LAST_DECRYPTED_FEATURES, last_decrypted)) {
1170 [ # # ]: 0 : pwallet->SetLastDecryptedFeatures(last_decrypted);
1171 : : }
1172 : : }
1173 : :
1174 : 10 : try {
1175 : : // Load wallet flags, so they are known when processing other records.
1176 : : // The FLAGS key is absent during wallet creation.
1177 [ + - + - ]: 10 : if ((result = LoadWalletFlags(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result;
1178 : :
1179 : : #ifndef ENABLE_EXTERNAL_SIGNER
1180 : : if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
1181 : : pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");
1182 : : return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;
1183 : : }
1184 : : #endif
1185 : :
1186 : : // Load legacy wallet keys
1187 [ + - + - ]: 10 : result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result);
1188 : :
1189 : : // Load descriptors
1190 [ + - + - ]: 10 : result = std::max(LoadDescriptorWalletRecords(pwallet, *m_batch, last_client), result);
1191 : : // Early return if there are unknown descriptors. Later loading of ACTIVEINTERNALSPK and ACTIVEEXTERNALEXPK
1192 : : // may reference the unknown descriptor's ID which can result in a misleading corruption error
1193 : : // when in reality the wallet is simply too new.
1194 [ + + ]: 10 : if (result == DBErrors::UNKNOWN_DESCRIPTOR) return result;
1195 : :
1196 : : // Load address book
1197 [ + - + + ]: 9 : result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result);
1198 : :
1199 : : // Load SPKMs
1200 [ + - + + ]: 9 : result = std::max(LoadActiveSPKMs(pwallet, *m_batch), result);
1201 : :
1202 : : // Load decryption keys
1203 [ + - + + ]: 9 : result = std::max(LoadDecryptionKeys(pwallet, *m_batch), result);
1204 : :
1205 : : // Load tx records
1206 [ + - + + ]: 16 : result = std::max(LoadTxRecords(pwallet, *m_batch, any_unordered), result);
1207 [ - - ]: 0 : } catch (std::runtime_error& e) {
1208 : : // Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions.
1209 : : // Any uncaught exceptions will be caught here and treated as critical.
1210 : : // Catch std::runtime_error specifically as many functions throw these and they at least have some message that
1211 : : // we can log
1212 [ - - ]: 0 : pwallet->WalletLogPrintf("%s\n", e.what());
1213 : 0 : result = DBErrors::CORRUPT;
1214 : 0 : } catch (...) {
1215 : : // All other exceptions are still problematic, but we can't log them
1216 : 0 : result = DBErrors::CORRUPT;
1217 [ - - ]: 0 : }
1218 : :
1219 : : // Any wallet corruption at all: skip any rewriting or
1220 : : // upgrading, we don't want to make it worse.
1221 [ + + ]: 9 : if (result != DBErrors::LOAD_OK)
1222 : : return result;
1223 : :
1224 [ - + ]: 7 : if (any_unordered)
1225 [ # # ]: 0 : result = pwallet->ReorderTransactions();
1226 : :
1227 : : // Upgrade all of the descriptor caches to cache the last hardened xpub
1228 : : // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
1229 : 7 : try {
1230 [ + - ]: 7 : pwallet->UpgradeDescriptorCache();
1231 : 0 : } catch (...) {
1232 : 0 : result = DBErrors::CORRUPT;
1233 [ - - ]: 0 : }
1234 : :
1235 : : // Since it was accidentally possible to "encrypt" a wallet with private keys disabled, we should check if this is
1236 : : // such a wallet and remove the encryption key records to avoid any future issues.
1237 : : // Although wallets without private keys should not have *ckey records, we should double check that.
1238 : : // Removing the mkey records is only safe if there are no *ckey records.
1239 [ + - - + : 7 : if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && pwallet->HasEncryptionKeys() && !pwallet->HaveCryptedKeys()) {
- - - - -
- - - ]
1240 [ # # ]: 0 : pwallet->WalletLogPrintf("Detected extraneous encryption keys in this wallet without private keys. Removing extraneous encryption keys.\n");
1241 [ # # # # ]: 0 : for (const auto& [id, _] : pwallet->mapMasterKeys) {
1242 [ # # # # ]: 0 : if (!EraseMasterKey(id)) {
1243 [ # # ]: 0 : pwallet->WalletLogPrintf("Error: Unable to remove extraneous encryption key '%u'. Wallet corrupt.\n", id);
1244 : : return DBErrors::CORRUPT;
1245 : : }
1246 : : }
1247 : 0 : pwallet->mapMasterKeys.clear();
1248 : : }
1249 : :
1250 : : // Discard stale decryption features before updating the version, so that a locked
1251 : : // reload cannot restore the features recorded before a downgrade.
1252 [ - + - - : 7 : if (last_client < VERSION_LAST_CLIENT_FEATURES && m_batch->Exists(DBKeys::LAST_DECRYPTED_FEATURES)) {
- - ]
1253 [ # # ]: 0 : if (!EraseIC(DBKeys::LAST_DECRYPTED_FEATURES)) {
1254 [ # # ]: 0 : pwallet->WalletLogPrintf("Error: Unable to erase last decrypted client features.\n");
1255 : : return DBErrors::LOAD_FAIL;
1256 : : }
1257 : : }
1258 : :
1259 : : // Record the current client version as the last version to successfully open this wallet file
1260 : : // This must always be done after all automatic upgrades so that those upgrades can be performed
1261 : : // in an upgrade-downgrade-upgrade scenario.
1262 [ + + - + ]: 7 : if (!has_last_client || last_client != VERSION_LATEST) {
1263 [ + - - + ]: 2 : if (!WriteLastOpenedVersion()) {
1264 [ # # ]: 0 : pwallet->WalletLogPrintf("Error: Unable to write the last opened version. Wallet corrupt.\n");
1265 : : return DBErrors::CORRUPT;
1266 : : }
1267 : : }
1268 : : // Record the current client features as the features of the last client to successfully open this wallet file.
1269 [ + + - + ]: 7 : if (!last_client_features || *last_client_features != WALLET_CLIENT_FEATURES) {
1270 [ + - - + ]: 2 : if (!WriteLastOpenedFeatures()) {
1271 [ # # ]: 0 : pwallet->WalletLogPrintf("Error: Unable to write the last opened features. Wallet corrupt.\n");
1272 : : return DBErrors::CORRUPT;
1273 : : }
1274 : : }
1275 : :
1276 : 7 : return result;
1277 : 10 : }
1278 : :
1279 : 65 : static bool RunWithinTxn(WalletBatch& batch, std::string_view process_desc, const std::function<bool(WalletBatch&)>& func)
1280 : : {
1281 [ - + ]: 65 : if (!batch.TxnBegin()) {
1282 [ # # ]: 0 : LogDebug(BCLog::WALLETDB, "Error: cannot create db txn for %s\n", process_desc);
1283 : 0 : return false;
1284 : : }
1285 : :
1286 : : // Run procedure
1287 [ + + ]: 65 : if (!func(batch)) {
1288 [ + - ]: 3 : LogDebug(BCLog::WALLETDB, "Error: %s failed\n", process_desc);
1289 : 3 : batch.TxnAbort();
1290 : 3 : return false;
1291 : : }
1292 : :
1293 [ + + ]: 62 : if (!batch.TxnCommit()) {
1294 [ + - ]: 1 : LogDebug(BCLog::WALLETDB, "Error: cannot commit db txn for %s\n", process_desc);
1295 : 1 : return false;
1296 : : }
1297 : :
1298 : : // All good
1299 : : return true;
1300 : : }
1301 : :
1302 : 65 : bool RunWithinTxn(WalletDatabase& database, std::string_view process_desc, const std::function<bool(WalletBatch&)>& func)
1303 : : {
1304 : 65 : WalletBatch batch(database);
1305 [ + - ]: 65 : return RunWithinTxn(batch, process_desc, func);
1306 : 65 : }
1307 : :
1308 : 3 : bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
1309 : : {
1310 [ + - + - ]: 9 : auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
1311 [ + + + - : 6 : return previously_spent ? WriteIC(key, std::string("1")) : EraseIC(key);
+ - ]
1312 : 3 : }
1313 : :
1314 : 4 : bool WalletBatch::WriteAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& receive_request)
1315 : : {
1316 [ + - + - : 16 : return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)), receive_request);
+ - ]
1317 : : }
1318 : :
1319 : 1 : bool WalletBatch::EraseAddressReceiveRequest(const CTxDestination& dest, const std::string& id)
1320 : : {
1321 [ + - + - : 4 : return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)));
+ - ]
1322 : : }
1323 : :
1324 : 1 : bool WalletBatch::EraseAddressData(const CTxDestination& dest)
1325 : : {
1326 : 1 : DataStream prefix;
1327 [ + - + - ]: 2 : prefix << DBKeys::DESTDATA << EncodeDestination(dest);
1328 [ - + + - ]: 1 : return m_batch->ErasePrefix(prefix);
1329 : 1 : }
1330 : :
1331 : 484 : bool WalletBatch::WriteWalletFlags(const uint64_t flags)
1332 : : {
1333 : 484 : return WriteIC(DBKeys::FLAGS, flags);
1334 : : }
1335 : :
1336 : 22 : bool WalletBatch::WriteLastOpenedVersion()
1337 : : {
1338 : 22 : return WriteIC(DBKeys::VERSION, VERSION_LATEST);
1339 : : }
1340 : :
1341 : 22 : bool WalletBatch::WriteLastOpenedFeatures()
1342 : : {
1343 : 22 : return WriteIC(DBKeys::LAST_OPENED_FEATURES, WALLET_CLIENT_FEATURES);
1344 : : }
1345 : :
1346 : 10 : bool WalletBatch::WriteLastDecryptedFeatures()
1347 : : {
1348 : 10 : return WriteIC(DBKeys::LAST_DECRYPTED_FEATURES, WALLET_CLIENT_FEATURES);
1349 : : }
1350 : :
1351 : 0 : bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types)
1352 : : {
1353 : 0 : return std::all_of(types.begin(), types.end(), [&](const std::string& type) {
1354 [ # # # # : 0 : return m_batch->ErasePrefix(DataStream() << type);
# # ]
1355 : 0 : });
1356 : : }
1357 : :
1358 : 7166 : bool WalletBatch::TxnBegin()
1359 : : {
1360 : 7166 : return m_batch->TxnBegin();
1361 : : }
1362 : :
1363 : 7161 : bool WalletBatch::TxnCommit()
1364 : : {
1365 : 7161 : bool res = m_batch->TxnCommit();
1366 [ + + ]: 7161 : if (res) {
1367 [ + + ]: 7216 : for (const auto& listener : m_txn_listeners) {
1368 : 57 : listener.on_commit();
1369 : : }
1370 : : // txn finished, clear listeners
1371 : 7159 : m_txn_listeners.clear();
1372 : : }
1373 : 7161 : return res;
1374 : : }
1375 : :
1376 : 6 : bool WalletBatch::TxnAbort()
1377 : : {
1378 : 6 : bool res = m_batch->TxnAbort();
1379 [ + - ]: 6 : if (res) {
1380 [ + + ]: 7 : for (const auto& listener : m_txn_listeners) {
1381 : 1 : listener.on_abort();
1382 : : }
1383 : : // txn finished, clear listeners
1384 : 6 : m_txn_listeners.clear();
1385 : : }
1386 : 6 : return res;
1387 : : }
1388 : :
1389 : 67 : void WalletBatch::RegisterTxnListener(DbTxnListener l)
1390 : : {
1391 [ - + ]: 67 : assert(m_batch->HasActiveTxn());
1392 : 67 : m_txn_listeners.emplace_back(std::move(l));
1393 : 67 : }
1394 : :
1395 : 19 : std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
1396 : : {
1397 : 19 : bool exists;
1398 : 19 : try {
1399 [ + - ]: 19 : exists = fs::symlink_status(path).type() != fs::file_type::not_found;
1400 [ - - ]: 0 : } catch (const fs::filesystem_error& e) {
1401 [ - - - - : 0 : error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), e.code().message()));
- - - - ]
1402 : 0 : status = DatabaseStatus::FAILED_BAD_PATH;
1403 : 0 : return nullptr;
1404 : 0 : }
1405 : :
1406 : 19 : std::optional<DatabaseFormat> format;
1407 [ + + ]: 19 : if (exists) {
1408 [ + - - + ]: 36 : if (IsBDBFile(BDBDataFile(path))) {
1409 : 0 : format = DatabaseFormat::BERKELEY_RO;
1410 : : }
1411 [ + - + + ]: 36 : if (IsSQLiteFile(SQLiteDataFile(path))) {
1412 [ - + ]: 5 : if (format) {
1413 [ # # # # : 0 : error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", fs::PathToString(path)));
# # ]
1414 : 0 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1415 : 0 : return nullptr;
1416 : : }
1417 : 5 : format = DatabaseFormat::SQLITE;
1418 : : }
1419 [ - + ]: 1 : } else if (options.require_existing) {
1420 [ # # # # : 0 : error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path)));
# # ]
1421 : 0 : status = DatabaseStatus::FAILED_NOT_FOUND;
1422 : 0 : return nullptr;
1423 : : }
1424 : :
1425 [ + + + + ]: 19 : if (!format && options.require_existing) {
1426 [ - + + - : 15 : error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path)));
+ - ]
1427 : 5 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1428 : 5 : return nullptr;
1429 : : }
1430 : :
1431 [ + + + - ]: 14 : if (format && options.require_create) {
1432 [ # # # # : 0 : error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path)));
# # ]
1433 : 0 : status = DatabaseStatus::FAILED_ALREADY_EXISTS;
1434 : 0 : return nullptr;
1435 : : }
1436 : :
1437 : : // BERKELEY_RO can only be opened if require_format was set, which only occurs in migration.
1438 [ + + - + : 14 : if (format && format == DatabaseFormat::BERKELEY_RO && (!options.require_format || options.require_format != DatabaseFormat::BERKELEY_RO)) {
- - - - ]
1439 [ # # # # : 0 : error = Untranslated(strprintf("Failed to open database path '%s'. The wallet appears to be a Legacy wallet, please use the wallet migration tool (migratewallet RPC or the GUI option).", fs::PathToString(path)));
# # ]
1440 : 0 : status = DatabaseStatus::FAILED_LEGACY_DISABLED;
1441 : 0 : return nullptr;
1442 : : }
1443 : :
1444 : : // A db already exists so format is set, but options also specifies the format, so make sure they agree
1445 [ + + + + : 14 : if (format && options.require_format && format != options.require_format) {
+ - ]
1446 [ # # # # : 0 : error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", fs::PathToString(path)));
# # ]
1447 : 0 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1448 : 0 : return nullptr;
1449 : : }
1450 : :
1451 : : // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
1452 [ + + + + ]: 14 : if (!format && options.require_format) format = options.require_format;
1453 : :
1454 [ + + ]: 14 : if (!format) {
1455 : 8 : format = DatabaseFormat::SQLITE;
1456 : : }
1457 : :
1458 [ + - ]: 14 : if (format == DatabaseFormat::SQLITE) {
1459 : 14 : return MakeSQLiteDatabase(path, options, status, error);
1460 : : }
1461 : :
1462 [ # # ]: 0 : if (format == DatabaseFormat::BERKELEY_RO) {
1463 : 0 : return MakeBerkeleyRODatabase(path, options, status, error);
1464 : : }
1465 : :
1466 [ # # ]: 0 : error = Untranslated(STR_INTERNAL_BUG("Could not determine wallet format"));
1467 : 0 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1468 : 0 : return nullptr;
1469 : : }
1470 : : } // namespace wallet
|