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