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