1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2018 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 <wallet/walletdb.h>
7
8 #include <consensus/tx_verify.h>
9 #include <consensus/validation.h>
10 #include <fs.h>
11 #include <key_io.h>
12 #include <protocol.h>
13 #include <serialize.h>
14 #include <sync.h>
15 #include <util/system.h>
16 #include <util/time.h>
17 #include <wallet/wallet.h>
18
19 #include <atomic>
20 #include <string>
21
22 #include <boost/thread.hpp>
23
24 //
25 // WalletBatch
26 //
27
WriteName(const std::string & strAddress,const std::string & strName)28 bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
29 {
30 return WriteIC(std::make_pair(std::string("name"), strAddress), strName);
31 }
32
EraseName(const std::string & strAddress)33 bool WalletBatch::EraseName(const std::string& strAddress)
34 {
35 // This should only be used for sending addresses, never for receiving addresses,
36 // receiving addresses must always have an address book entry if they're not change return.
37 return EraseIC(std::make_pair(std::string("name"), strAddress));
38 }
39
WritePurpose(const std::string & strAddress,const std::string & strPurpose)40 bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
41 {
42 return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose);
43 }
44
ErasePurpose(const std::string & strAddress)45 bool WalletBatch::ErasePurpose(const std::string& strAddress)
46 {
47 return EraseIC(std::make_pair(std::string("purpose"), strAddress));
48 }
49
WriteTx(const CWalletTx & wtx)50 bool WalletBatch::WriteTx(const CWalletTx& wtx)
51 {
52 return WriteIC(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
53 }
54
EraseTx(uint256 hash)55 bool WalletBatch::EraseTx(uint256 hash)
56 {
57 return EraseIC(std::make_pair(std::string("tx"), hash));
58 }
59
WriteKeyMetadata(const CKeyMetadata & meta,const CPubKey & pubkey,const bool overwrite)60 bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
61 {
62 return WriteIC(std::make_pair(std::string("keymeta"), pubkey), meta, overwrite);
63 }
64
WriteKey(const CPubKey & vchPubKey,const CPrivKey & vchPrivKey,const CKeyMetadata & keyMeta)65 bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
66 {
67 if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
68 return false;
69 }
70
71 // hash pubkey/privkey to accelerate wallet load
72 std::vector<unsigned char> vchKey;
73 vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
74 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
75 vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
76
77 return WriteIC(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
78 }
79
WriteCryptedKey(const CPubKey & vchPubKey,const std::vector<unsigned char> & vchCryptedSecret,const CKeyMetadata & keyMeta)80 bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
81 const std::vector<unsigned char>& vchCryptedSecret,
82 const CKeyMetadata &keyMeta)
83 {
84 if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
85 return false;
86 }
87
88 if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) {
89 return false;
90 }
91 EraseIC(std::make_pair(std::string("key"), vchPubKey));
92 EraseIC(std::make_pair(std::string("wkey"), vchPubKey));
93 return true;
94 }
95
WriteMasterKey(unsigned int nID,const CMasterKey & kMasterKey)96 bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
97 {
98 return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
99 }
100
WriteCScript(const uint160 & hash,const CScript & redeemScript)101 bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
102 {
103 return WriteIC(std::make_pair(std::string("cscript"), hash), redeemScript, false);
104 }
105
WriteWatchOnly(const CScript & dest,const CKeyMetadata & keyMeta)106 bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
107 {
108 if (!WriteIC(std::make_pair(std::string("watchmeta"), dest), keyMeta)) {
109 return false;
110 }
111 return WriteIC(std::make_pair(std::string("watchs"), dest), '1');
112 }
113
EraseWatchOnly(const CScript & dest)114 bool WalletBatch::EraseWatchOnly(const CScript &dest)
115 {
116 if (!EraseIC(std::make_pair(std::string("watchmeta"), dest))) {
117 return false;
118 }
119 return EraseIC(std::make_pair(std::string("watchs"), dest));
120 }
121
WriteBestBlock(const CBlockLocator & locator)122 bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
123 {
124 WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
125 return WriteIC(std::string("bestblock_nomerkle"), locator);
126 }
127
ReadBestBlock(CBlockLocator & locator)128 bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
129 {
130 if (m_batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
131 return m_batch.Read(std::string("bestblock_nomerkle"), locator);
132 }
133
WriteOrderPosNext(int64_t nOrderPosNext)134 bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
135 {
136 return WriteIC(std::string("orderposnext"), nOrderPosNext);
137 }
138
ReadPool(int64_t nPool,CKeyPool & keypool)139 bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
140 {
141 return m_batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
142 }
143
WritePool(int64_t nPool,const CKeyPool & keypool)144 bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
145 {
146 return WriteIC(std::make_pair(std::string("pool"), nPool), keypool);
147 }
148
ErasePool(int64_t nPool)149 bool WalletBatch::ErasePool(int64_t nPool)
150 {
151 return EraseIC(std::make_pair(std::string("pool"), nPool));
152 }
153
WriteMinVersion(int nVersion)154 bool WalletBatch::WriteMinVersion(int nVersion)
155 {
156 return WriteIC(std::string("minversion"), nVersion);
157 }
158
159 class CWalletScanState {
160 public:
161 unsigned int nKeys{0};
162 unsigned int nCKeys{0};
163 unsigned int nWatchKeys{0};
164 unsigned int nKeyMeta{0};
165 unsigned int m_unknown_records{0};
166 bool fIsEncrypted{false};
167 bool fAnyUnordered{false};
168 int nFileVersion{0};
169 std::vector<uint256> vWalletUpgrade;
170
CWalletScanState()171 CWalletScanState() {
172 }
173 };
174
175 static bool
ReadKeyValue(CWallet * pwallet,CDataStream & ssKey,CDataStream & ssValue,CWalletScanState & wss,std::string & strType,std::string & strErr)176 ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
177 CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
178 {
179 try {
180 // Unserialize
181 // Taking advantage of the fact that pair serialization
182 // is just the two items serialized one after the other
183 ssKey >> strType;
184 if (strType == "name")
185 {
186 std::string strAddress;
187 ssKey >> strAddress;
188 ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name;
189 }
190 else if (strType == "purpose")
191 {
192 std::string strAddress;
193 ssKey >> strAddress;
194 ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose;
195 }
196 else if (strType == "tx")
197 {
198 uint256 hash;
199 ssKey >> hash;
200 CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
201 ssValue >> wtx;
202 CValidationState state;
203 if (!(CheckTransaction(*wtx.tx, state) && (wtx.GetHash() == hash) && state.IsValid()))
204 return false;
205
206 // Undo serialize changes in 31600
207 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
208 {
209 if (!ssValue.empty())
210 {
211 char fTmp;
212 char fUnused;
213 std::string unused_string;
214 ssValue >> fTmp >> fUnused >> unused_string;
215 strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s",
216 wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
217 wtx.fTimeReceivedIsTxTime = fTmp;
218 }
219 else
220 {
221 strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
222 wtx.fTimeReceivedIsTxTime = 0;
223 }
224 wss.vWalletUpgrade.push_back(hash);
225 }
226
227 if (wtx.nOrderPos == -1)
228 wss.fAnyUnordered = true;
229
230 pwallet->LoadToWallet(wtx);
231 }
232 else if (strType == "watchs")
233 {
234 wss.nWatchKeys++;
235 CScript script;
236 ssKey >> script;
237 char fYes;
238 ssValue >> fYes;
239 if (fYes == '1')
240 pwallet->LoadWatchOnly(script);
241 }
242 else if (strType == "key" || strType == "wkey")
243 {
244 CPubKey vchPubKey;
245 ssKey >> vchPubKey;
246 if (!vchPubKey.IsValid())
247 {
248 strErr = "Error reading wallet database: CPubKey corrupt";
249 return false;
250 }
251 CKey key;
252 CPrivKey pkey;
253 uint256 hash;
254
255 if (strType == "key")
256 {
257 wss.nKeys++;
258 ssValue >> pkey;
259 } else {
260 CWalletKey wkey;
261 ssValue >> wkey;
262 pkey = wkey.vchPrivKey;
263 }
264
265 // Old wallets store keys as "key" [pubkey] => [privkey]
266 // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
267 // using EC operations as a checksum.
268 // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
269 // remaining backwards-compatible.
270 try
271 {
272 ssValue >> hash;
273 }
274 catch (...) {}
275
276 bool fSkipCheck = false;
277
278 if (!hash.IsNull())
279 {
280 // hash pubkey/privkey to accelerate wallet load
281 std::vector<unsigned char> vchKey;
282 vchKey.reserve(vchPubKey.size() + pkey.size());
283 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
284 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
285
286 if (Hash(vchKey.begin(), vchKey.end()) != hash)
287 {
288 strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
289 return false;
290 }
291
292 fSkipCheck = true;
293 }
294
295 if (!key.Load(pkey, vchPubKey, fSkipCheck))
296 {
297 strErr = "Error reading wallet database: CPrivKey corrupt";
298 return false;
299 }
300 if (!pwallet->LoadKey(key, vchPubKey))
301 {
302 strErr = "Error reading wallet database: LoadKey failed";
303 return false;
304 }
305 }
306 else if (strType == "mkey")
307 {
308 unsigned int nID;
309 ssKey >> nID;
310 CMasterKey kMasterKey;
311 ssValue >> kMasterKey;
312 if(pwallet->mapMasterKeys.count(nID) != 0)
313 {
314 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
315 return false;
316 }
317 pwallet->mapMasterKeys[nID] = kMasterKey;
318 if (pwallet->nMasterKeyMaxID < nID)
319 pwallet->nMasterKeyMaxID = nID;
320 }
321 else if (strType == "ckey")
322 {
323 CPubKey vchPubKey;
324 ssKey >> vchPubKey;
325 if (!vchPubKey.IsValid())
326 {
327 strErr = "Error reading wallet database: CPubKey corrupt";
328 return false;
329 }
330 std::vector<unsigned char> vchPrivKey;
331 ssValue >> vchPrivKey;
332 wss.nCKeys++;
333
334 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
335 {
336 strErr = "Error reading wallet database: LoadCryptedKey failed";
337 return false;
338 }
339 wss.fIsEncrypted = true;
340 }
341 else if (strType == "keymeta")
342 {
343 CPubKey vchPubKey;
344 ssKey >> vchPubKey;
345 CKeyMetadata keyMeta;
346 ssValue >> keyMeta;
347 wss.nKeyMeta++;
348 pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
349 }
350 else if (strType == "watchmeta")
351 {
352 CScript script;
353 ssKey >> script;
354 CKeyMetadata keyMeta;
355 ssValue >> keyMeta;
356 wss.nKeyMeta++;
357 pwallet->LoadScriptMetadata(CScriptID(script), keyMeta);
358 }
359 else if (strType == "defaultkey")
360 {
361 // We don't want or need the default key, but if there is one set,
362 // we want to make sure that it is valid so that we can detect corruption
363 CPubKey vchPubKey;
364 ssValue >> vchPubKey;
365 if (!vchPubKey.IsValid()) {
366 strErr = "Error reading wallet database: Default Key corrupt";
367 return false;
368 }
369 }
370 else if (strType == "pool")
371 {
372 int64_t nIndex;
373 ssKey >> nIndex;
374 CKeyPool keypool;
375 ssValue >> keypool;
376
377 pwallet->LoadKeyPool(nIndex, keypool);
378 }
379 else if (strType == "version")
380 {
381 ssValue >> wss.nFileVersion;
382 if (wss.nFileVersion == 10300)
383 wss.nFileVersion = 300;
384 }
385 else if (strType == "cscript")
386 {
387 uint160 hash;
388 ssKey >> hash;
389 CScript script;
390 ssValue >> script;
391 if (!pwallet->LoadCScript(script))
392 {
393 strErr = "Error reading wallet database: LoadCScript failed";
394 return false;
395 }
396 }
397 else if (strType == "orderposnext")
398 {
399 ssValue >> pwallet->nOrderPosNext;
400 }
401 else if (strType == "destdata")
402 {
403 std::string strAddress, strKey, strValue;
404 ssKey >> strAddress;
405 ssKey >> strKey;
406 ssValue >> strValue;
407 pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue);
408 }
409 else if (strType == "hdchain")
410 {
411 CHDChain chain;
412 ssValue >> chain;
413 pwallet->SetHDChain(chain, true);
414 } else if (strType == "flags") {
415 uint64_t flags;
416 ssValue >> flags;
417 if (!pwallet->SetWalletFlags(flags, true)) {
418 strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
419 return false;
420 }
421 } else if (strType != "bestblock" && strType != "bestblock_nomerkle" &&
422 strType != "minversion" && strType != "acentry") {
423 wss.m_unknown_records++;
424 }
425 } catch (...)
426 {
427 return false;
428 }
429 return true;
430 }
431
IsKeyType(const std::string & strType)432 bool WalletBatch::IsKeyType(const std::string& strType)
433 {
434 return (strType== "key" || strType == "wkey" ||
435 strType == "mkey" || strType == "ckey");
436 }
437
LoadWallet(CWallet * pwallet)438 DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
439 {
440 CWalletScanState wss;
441 bool fNoncriticalErrors = false;
442 DBErrors result = DBErrors::LOAD_OK;
443
444 LOCK(pwallet->cs_wallet);
445 try {
446 int nMinVersion = 0;
447 if (m_batch.Read((std::string)"minversion", nMinVersion))
448 {
449 if (nMinVersion > FEATURE_LATEST)
450 return DBErrors::TOO_NEW;
451 pwallet->LoadMinVersion(nMinVersion);
452 }
453
454 // Get cursor
455 Dbc* pcursor = m_batch.GetCursor();
456 if (!pcursor)
457 {
458 pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
459 return DBErrors::CORRUPT;
460 }
461
462 while (true)
463 {
464 // Read next record
465 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
466 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
467 int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
468 if (ret == DB_NOTFOUND)
469 break;
470 else if (ret != 0)
471 {
472 pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
473 return DBErrors::CORRUPT;
474 }
475
476 // Try to be tolerant of single corrupt records:
477 std::string strType, strErr;
478 if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
479 {
480 // losing keys is considered a catastrophic error, anything else
481 // we assume the user can live with:
482 if (IsKeyType(strType) || strType == "defaultkey") {
483 result = DBErrors::CORRUPT;
484 } else if(strType == "flags") {
485 // reading the wallet flags can only fail if unknown flags are present
486 result = DBErrors::TOO_NEW;
487 } else {
488 // Leave other errors alone, if we try to fix them we might make things worse.
489 fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
490 if (strType == "tx")
491 // Rescan if there is a bad transaction record:
492 gArgs.SoftSetBoolArg("-rescan", true);
493 }
494 }
495 if (!strErr.empty())
496 pwallet->WalletLogPrintf("%s\n", strErr);
497 }
498 pcursor->close();
499 }
500 catch (const boost::thread_interrupted&) {
501 throw;
502 }
503 catch (...) {
504 result = DBErrors::CORRUPT;
505 }
506
507 if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
508 result = DBErrors::NONCRITICAL_ERROR;
509
510 // Any wallet corruption at all: skip any rewriting or
511 // upgrading, we don't want to make it worse.
512 if (result != DBErrors::LOAD_OK)
513 return result;
514
515 pwallet->WalletLogPrintf("nFileVersion = %d\n", wss.nFileVersion);
516
517 pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n",
518 wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
519
520 // nTimeFirstKey is only reliable if all keys have metadata
521 if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta)
522 pwallet->UpdateTimeFirstKey(1);
523
524 for (const uint256& hash : wss.vWalletUpgrade)
525 WriteTx(pwallet->mapWallet.at(hash));
526
527 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
528 if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
529 return DBErrors::NEED_REWRITE;
530
531 if (wss.nFileVersion < CLIENT_VERSION) // Update
532 WriteVersion(CLIENT_VERSION);
533
534 if (wss.fAnyUnordered)
535 result = pwallet->ReorderTransactions();
536
537 // Upgrade all of the wallet keymetadata to have the hd master key id
538 // This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
539 try {
540 pwallet->UpgradeKeyMetadata();
541 } catch (...) {
542 result = DBErrors::CORRUPT;
543 }
544
545 return result;
546 }
547
FindWalletTx(std::vector<uint256> & vTxHash,std::vector<CWalletTx> & vWtx)548 DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx)
549 {
550 DBErrors result = DBErrors::LOAD_OK;
551
552 try {
553 int nMinVersion = 0;
554 if (m_batch.Read((std::string)"minversion", nMinVersion))
555 {
556 if (nMinVersion > FEATURE_LATEST)
557 return DBErrors::TOO_NEW;
558 }
559
560 // Get cursor
561 Dbc* pcursor = m_batch.GetCursor();
562 if (!pcursor)
563 {
564 LogPrintf("Error getting wallet database cursor\n");
565 return DBErrors::CORRUPT;
566 }
567
568 while (true)
569 {
570 // Read next record
571 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
572 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
573 int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
574 if (ret == DB_NOTFOUND)
575 break;
576 else if (ret != 0)
577 {
578 LogPrintf("Error reading next record from wallet database\n");
579 return DBErrors::CORRUPT;
580 }
581
582 std::string strType;
583 ssKey >> strType;
584 if (strType == "tx") {
585 uint256 hash;
586 ssKey >> hash;
587
588 CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
589 ssValue >> wtx;
590
591 vTxHash.push_back(hash);
592 vWtx.push_back(wtx);
593 }
594 }
595 pcursor->close();
596 }
597 catch (const boost::thread_interrupted&) {
598 throw;
599 }
600 catch (...) {
601 result = DBErrors::CORRUPT;
602 }
603
604 return result;
605 }
606
ZapSelectTx(std::vector<uint256> & vTxHashIn,std::vector<uint256> & vTxHashOut)607 DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
608 {
609 // build list of wallet TXs and hashes
610 std::vector<uint256> vTxHash;
611 std::vector<CWalletTx> vWtx;
612 DBErrors err = FindWalletTx(vTxHash, vWtx);
613 if (err != DBErrors::LOAD_OK) {
614 return err;
615 }
616
617 std::sort(vTxHash.begin(), vTxHash.end());
618 std::sort(vTxHashIn.begin(), vTxHashIn.end());
619
620 // erase each matching wallet TX
621 bool delerror = false;
622 std::vector<uint256>::iterator it = vTxHashIn.begin();
623 for (const uint256& hash : vTxHash) {
624 while (it < vTxHashIn.end() && (*it) < hash) {
625 it++;
626 }
627 if (it == vTxHashIn.end()) {
628 break;
629 }
630 else if ((*it) == hash) {
631 if(!EraseTx(hash)) {
632 LogPrint(BCLog::DB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
633 delerror = true;
634 }
635 vTxHashOut.push_back(hash);
636 }
637 }
638
639 if (delerror) {
640 return DBErrors::CORRUPT;
641 }
642 return DBErrors::LOAD_OK;
643 }
644
ZapWalletTx(std::vector<CWalletTx> & vWtx)645 DBErrors WalletBatch::ZapWalletTx(std::vector<CWalletTx>& vWtx)
646 {
647 // build list of wallet TXs
648 std::vector<uint256> vTxHash;
649 DBErrors err = FindWalletTx(vTxHash, vWtx);
650 if (err != DBErrors::LOAD_OK)
651 return err;
652
653 // erase each wallet TX
654 for (const uint256& hash : vTxHash) {
655 if (!EraseTx(hash))
656 return DBErrors::CORRUPT;
657 }
658
659 return DBErrors::LOAD_OK;
660 }
661
MaybeCompactWalletDB()662 void MaybeCompactWalletDB()
663 {
664 static std::atomic<bool> fOneThread(false);
665 if (fOneThread.exchange(true)) {
666 return;
667 }
668 if (!gArgs.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
669 return;
670 }
671
672 for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
673 WalletDatabase& dbh = pwallet->GetDBHandle();
674
675 unsigned int nUpdateCounter = dbh.nUpdateCounter;
676
677 if (dbh.nLastSeen != nUpdateCounter) {
678 dbh.nLastSeen = nUpdateCounter;
679 dbh.nLastWalletUpdate = GetTime();
680 }
681
682 if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
683 if (BerkeleyBatch::PeriodicFlush(dbh)) {
684 dbh.nLastFlushed = nUpdateCounter;
685 }
686 }
687 }
688
689 fOneThread = false;
690 }
691
692 //
693 // Try to (very carefully!) recover wallet file if there is a problem.
694 //
Recover(const fs::path & wallet_path,void * callbackDataIn,bool (* recoverKVcallback)(void * callbackData,CDataStream ssKey,CDataStream ssValue),std::string & out_backup_filename)695 bool WalletBatch::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
696 {
697 return BerkeleyBatch::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
698 }
699
Recover(const fs::path & wallet_path,std::string & out_backup_filename)700 bool WalletBatch::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
701 {
702 // recover without a key filter callback
703 // results in recovering all record types
704 return WalletBatch::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
705 }
706
RecoverKeysOnlyFilter(void * callbackData,CDataStream ssKey,CDataStream ssValue)707 bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
708 {
709 CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
710 CWalletScanState dummyWss;
711 std::string strType, strErr;
712 bool fReadOK;
713 {
714 // Required in LoadKeyMetadata():
715 LOCK(dummyWallet->cs_wallet);
716 fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
717 dummyWss, strType, strErr);
718 }
719 if (!IsKeyType(strType) && strType != "hdchain")
720 return false;
721 if (!fReadOK)
722 {
723 LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
724 return false;
725 }
726
727 return true;
728 }
729
VerifyEnvironment(const fs::path & wallet_path,std::string & errorStr)730 bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr)
731 {
732 return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);
733 }
734
VerifyDatabaseFile(const fs::path & wallet_path,std::string & warningStr,std::string & errorStr)735 bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr)
736 {
737 return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warningStr, errorStr, WalletBatch::Recover);
738 }
739
WriteDestData(const std::string & address,const std::string & key,const std::string & value)740 bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
741 {
742 return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
743 }
744
EraseDestData(const std::string & address,const std::string & key)745 bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
746 {
747 return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
748 }
749
750
WriteHDChain(const CHDChain & chain)751 bool WalletBatch::WriteHDChain(const CHDChain& chain)
752 {
753 return WriteIC(std::string("hdchain"), chain);
754 }
755
WriteWalletFlags(const uint64_t flags)756 bool WalletBatch::WriteWalletFlags(const uint64_t flags)
757 {
758 return WriteIC(std::string("flags"), flags);
759 }
760
TxnBegin()761 bool WalletBatch::TxnBegin()
762 {
763 return m_batch.TxnBegin();
764 }
765
TxnCommit()766 bool WalletBatch::TxnCommit()
767 {
768 return m_batch.TxnCommit();
769 }
770
TxnAbort()771 bool WalletBatch::TxnAbort()
772 {
773 return m_batch.TxnAbort();
774 }
775
ReadVersion(int & nVersion)776 bool WalletBatch::ReadVersion(int& nVersion)
777 {
778 return m_batch.ReadVersion(nVersion);
779 }
780
WriteVersion(int nVersion)781 bool WalletBatch::WriteVersion(int nVersion)
782 {
783 return m_batch.WriteVersion(nVersion);
784 }
785