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