1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 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 "base58.h"
9 #include "consensus/validation.h"
10 #include "main.h" // For CheckTransaction
11 #include "protocol.h"
12 #include "serialize.h"
13 #include "sync.h"
14 #include "util.h"
15 #include "utiltime.h"
16 #include "wallet/wallet.h"
17 
18 #include <boost/version.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/foreach.hpp>
21 #include <boost/scoped_ptr.hpp>
22 #include <boost/thread.hpp>
23 
24 using namespace std;
25 
26 static uint64_t nAccountingEntryNumber = 0;
27 
28 //
29 // CWalletDB
30 //
31 
WriteName(const string & strAddress,const string & strName)32 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
33 {
34     nWalletDBUpdated++;
35     return Write(make_pair(string("name"), strAddress), strName);
36 }
37 
EraseName(const string & strAddress)38 bool CWalletDB::EraseName(const string& strAddress)
39 {
40     // This should only be used for sending addresses, never for receiving addresses,
41     // receiving addresses must always have an address book entry if they're not change return.
42     nWalletDBUpdated++;
43     return Erase(make_pair(string("name"), strAddress));
44 }
45 
WritePurpose(const string & strAddress,const string & strPurpose)46 bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose)
47 {
48     nWalletDBUpdated++;
49     return Write(make_pair(string("purpose"), strAddress), strPurpose);
50 }
51 
ErasePurpose(const string & strPurpose)52 bool CWalletDB::ErasePurpose(const string& strPurpose)
53 {
54     nWalletDBUpdated++;
55     return Erase(make_pair(string("purpose"), strPurpose));
56 }
57 
WriteTx(const CWalletTx & wtx)58 bool CWalletDB::WriteTx(const CWalletTx& wtx)
59 {
60     nWalletDBUpdated++;
61     return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
62 }
63 
EraseTx(uint256 hash)64 bool CWalletDB::EraseTx(uint256 hash)
65 {
66     nWalletDBUpdated++;
67     return Erase(std::make_pair(std::string("tx"), hash));
68 }
69 
WriteKey(const CPubKey & vchPubKey,const CPrivKey & vchPrivKey,const CKeyMetadata & keyMeta)70 bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
71 {
72     nWalletDBUpdated++;
73 
74     if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
75                keyMeta, false))
76         return false;
77 
78     // hash pubkey/privkey to accelerate wallet load
79     std::vector<unsigned char> vchKey;
80     vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
81     vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
82     vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
83 
84     return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
85 }
86 
WriteCryptedKey(const CPubKey & vchPubKey,const std::vector<unsigned char> & vchCryptedSecret,const CKeyMetadata & keyMeta)87 bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
88                                 const std::vector<unsigned char>& vchCryptedSecret,
89                                 const CKeyMetadata &keyMeta)
90 {
91     const bool fEraseUnencryptedKey = true;
92     nWalletDBUpdated++;
93 
94     if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
95             keyMeta))
96         return false;
97 
98     if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
99         return false;
100     if (fEraseUnencryptedKey)
101     {
102         Erase(std::make_pair(std::string("key"), vchPubKey));
103         Erase(std::make_pair(std::string("wkey"), vchPubKey));
104     }
105     return true;
106 }
107 
WriteMasterKey(unsigned int nID,const CMasterKey & kMasterKey)108 bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
109 {
110     nWalletDBUpdated++;
111     return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
112 }
113 
WriteCScript(const uint160 & hash,const CScript & redeemScript)114 bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
115 {
116     nWalletDBUpdated++;
117     return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
118 }
119 
WriteWatchOnly(const CScript & dest)120 bool CWalletDB::WriteWatchOnly(const CScript &dest)
121 {
122     nWalletDBUpdated++;
123     return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
124 }
125 
EraseWatchOnly(const CScript & dest)126 bool CWalletDB::EraseWatchOnly(const CScript &dest)
127 {
128     nWalletDBUpdated++;
129     return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
130 }
131 
WriteBestBlock(const CBlockLocator & locator)132 bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
133 {
134     nWalletDBUpdated++;
135     Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
136     return Write(std::string("bestblock_nomerkle"), locator);
137 }
138 
ReadBestBlock(CBlockLocator & locator)139 bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
140 {
141     if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
142     return Read(std::string("bestblock_nomerkle"), locator);
143 }
144 
WriteOrderPosNext(int64_t nOrderPosNext)145 bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
146 {
147     nWalletDBUpdated++;
148     return Write(std::string("orderposnext"), nOrderPosNext);
149 }
150 
WriteDefaultKey(const CPubKey & vchPubKey)151 bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
152 {
153     nWalletDBUpdated++;
154     return Write(std::string("defaultkey"), vchPubKey);
155 }
156 
ReadPool(int64_t nPool,CKeyPool & keypool)157 bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
158 {
159     return Read(std::make_pair(std::string("pool"), nPool), keypool);
160 }
161 
WritePool(int64_t nPool,const CKeyPool & keypool)162 bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
163 {
164     nWalletDBUpdated++;
165     return Write(std::make_pair(std::string("pool"), nPool), keypool);
166 }
167 
ErasePool(int64_t nPool)168 bool CWalletDB::ErasePool(int64_t nPool)
169 {
170     nWalletDBUpdated++;
171     return Erase(std::make_pair(std::string("pool"), nPool));
172 }
173 
WriteMinVersion(int nVersion)174 bool CWalletDB::WriteMinVersion(int nVersion)
175 {
176     return Write(std::string("minversion"), nVersion);
177 }
178 
ReadAccount(const string & strAccount,CAccount & account)179 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
180 {
181     account.SetNull();
182     return Read(make_pair(string("acc"), strAccount), account);
183 }
184 
WriteAccount(const string & strAccount,const CAccount & account)185 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
186 {
187     return Write(make_pair(string("acc"), strAccount), account);
188 }
189 
WriteAccountingEntry(const uint64_t nAccEntryNum,const CAccountingEntry & acentry)190 bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
191 {
192     return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
193 }
194 
WriteAccountingEntry_Backend(const CAccountingEntry & acentry)195 bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
196 {
197     return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
198 }
199 
GetAccountCreditDebit(const string & strAccount)200 CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount)
201 {
202     list<CAccountingEntry> entries;
203     ListAccountCreditDebit(strAccount, entries);
204 
205     CAmount nCreditDebit = 0;
206     BOOST_FOREACH (const CAccountingEntry& entry, entries)
207         nCreditDebit += entry.nCreditDebit;
208 
209     return nCreditDebit;
210 }
211 
ListAccountCreditDebit(const string & strAccount,list<CAccountingEntry> & entries)212 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
213 {
214     bool fAllAccounts = (strAccount == "*");
215 
216     Dbc* pcursor = GetCursor();
217     if (!pcursor)
218         throw runtime_error(std::string(__func__) + ": cannot create DB cursor");
219     unsigned int fFlags = DB_SET_RANGE;
220     while (true)
221     {
222         // Read next record
223         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
224         if (fFlags == DB_SET_RANGE)
225             ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0)));
226         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
227         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
228         fFlags = DB_NEXT;
229         if (ret == DB_NOTFOUND)
230             break;
231         else if (ret != 0)
232         {
233             pcursor->close();
234             throw runtime_error(std::string(__func__) + ": error scanning DB");
235         }
236 
237         // Unserialize
238         string strType;
239         ssKey >> strType;
240         if (strType != "acentry")
241             break;
242         CAccountingEntry acentry;
243         ssKey >> acentry.strAccount;
244         if (!fAllAccounts && acentry.strAccount != strAccount)
245             break;
246 
247         ssValue >> acentry;
248         ssKey >> acentry.nEntryNo;
249         entries.push_back(acentry);
250     }
251 
252     pcursor->close();
253 }
254 
ReorderTransactions(CWallet * pwallet)255 DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet)
256 {
257     LOCK(pwallet->cs_wallet);
258     // Old wallets didn't have any defined order for transactions
259     // Probably a bad idea to change the output of this
260 
261     // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
262     typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
263     typedef multimap<int64_t, TxPair > TxItems;
264     TxItems txByTime;
265 
266     for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
267     {
268         CWalletTx* wtx = &((*it).second);
269         txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
270     }
271     list<CAccountingEntry> acentries;
272     ListAccountCreditDebit("", acentries);
273     BOOST_FOREACH(CAccountingEntry& entry, acentries)
274     {
275         txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
276     }
277 
278     int64_t& nOrderPosNext = pwallet->nOrderPosNext;
279     nOrderPosNext = 0;
280     std::vector<int64_t> nOrderPosOffsets;
281     for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
282     {
283         CWalletTx *const pwtx = (*it).second.first;
284         CAccountingEntry *const pacentry = (*it).second.second;
285         int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
286 
287         if (nOrderPos == -1)
288         {
289             nOrderPos = nOrderPosNext++;
290             nOrderPosOffsets.push_back(nOrderPos);
291 
292             if (pwtx)
293             {
294                 if (!WriteTx(*pwtx))
295                     return DB_LOAD_FAIL;
296             }
297             else
298                 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
299                     return DB_LOAD_FAIL;
300         }
301         else
302         {
303             int64_t nOrderPosOff = 0;
304             BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets)
305             {
306                 if (nOrderPos >= nOffsetStart)
307                     ++nOrderPosOff;
308             }
309             nOrderPos += nOrderPosOff;
310             nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
311 
312             if (!nOrderPosOff)
313                 continue;
314 
315             // Since we're changing the order, write it back
316             if (pwtx)
317             {
318                 if (!WriteTx(*pwtx))
319                     return DB_LOAD_FAIL;
320             }
321             else
322                 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
323                     return DB_LOAD_FAIL;
324         }
325     }
326     WriteOrderPosNext(nOrderPosNext);
327 
328     return DB_LOAD_OK;
329 }
330 
331 class CWalletScanState {
332 public:
333     unsigned int nKeys;
334     unsigned int nCKeys;
335     unsigned int nKeyMeta;
336     bool fIsEncrypted;
337     bool fAnyUnordered;
338     int nFileVersion;
339     vector<uint256> vWalletUpgrade;
340 
CWalletScanState()341     CWalletScanState() {
342         nKeys = nCKeys = nKeyMeta = 0;
343         fIsEncrypted = false;
344         fAnyUnordered = false;
345         nFileVersion = 0;
346     }
347 };
348 
349 bool
ReadKeyValue(CWallet * pwallet,CDataStream & ssKey,CDataStream & ssValue,CWalletScanState & wss,string & strType,string & strErr)350 ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
351              CWalletScanState &wss, string& strType, string& strErr)
352 {
353     try {
354         // Unserialize
355         // Taking advantage of the fact that pair serialization
356         // is just the two items serialized one after the other
357         ssKey >> strType;
358         if (strType == "name")
359         {
360             string strAddress;
361             ssKey >> strAddress;
362             ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name;
363         }
364         else if (strType == "purpose")
365         {
366             string strAddress;
367             ssKey >> strAddress;
368             ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose;
369         }
370         else if (strType == "tx")
371         {
372             uint256 hash;
373             ssKey >> hash;
374             CWalletTx wtx;
375             ssValue >> wtx;
376             CValidationState state;
377             if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()))
378                 return false;
379 
380             // Undo serialize changes in 31600
381             if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
382             {
383                 if (!ssValue.empty())
384                 {
385                     char fTmp;
386                     char fUnused;
387                     ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
388                     strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
389                                        wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString());
390                     wtx.fTimeReceivedIsTxTime = fTmp;
391                 }
392                 else
393                 {
394                     strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
395                     wtx.fTimeReceivedIsTxTime = 0;
396                 }
397                 wss.vWalletUpgrade.push_back(hash);
398             }
399 
400             if (wtx.nOrderPos == -1)
401                 wss.fAnyUnordered = true;
402 
403             pwallet->AddToWallet(wtx, true, NULL);
404         }
405         else if (strType == "acentry")
406         {
407             string strAccount;
408             ssKey >> strAccount;
409             uint64_t nNumber;
410             ssKey >> nNumber;
411             if (nNumber > nAccountingEntryNumber)
412                 nAccountingEntryNumber = nNumber;
413 
414             if (!wss.fAnyUnordered)
415             {
416                 CAccountingEntry acentry;
417                 ssValue >> acentry;
418                 if (acentry.nOrderPos == -1)
419                     wss.fAnyUnordered = true;
420             }
421         }
422         else if (strType == "watchs")
423         {
424             CScript script;
425             ssKey >> *(CScriptBase*)(&script);
426             char fYes;
427             ssValue >> fYes;
428             if (fYes == '1')
429                 pwallet->LoadWatchOnly(script);
430 
431             // Watch-only addresses have no birthday information for now,
432             // so set the wallet birthday to the beginning of time.
433             pwallet->nTimeFirstKey = 1;
434         }
435         else if (strType == "key" || strType == "wkey")
436         {
437             CPubKey vchPubKey;
438             ssKey >> vchPubKey;
439             if (!vchPubKey.IsValid())
440             {
441                 strErr = "Error reading wallet database: CPubKey corrupt";
442                 return false;
443             }
444             CKey key;
445             CPrivKey pkey;
446             uint256 hash;
447 
448             if (strType == "key")
449             {
450                 wss.nKeys++;
451                 ssValue >> pkey;
452             } else {
453                 CWalletKey wkey;
454                 ssValue >> wkey;
455                 pkey = wkey.vchPrivKey;
456             }
457 
458             // Old wallets store keys as "key" [pubkey] => [privkey]
459             // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
460             // using EC operations as a checksum.
461             // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
462             // remaining backwards-compatible.
463             try
464             {
465                 ssValue >> hash;
466             }
467             catch (...) {}
468 
469             bool fSkipCheck = false;
470 
471             if (!hash.IsNull())
472             {
473                 // hash pubkey/privkey to accelerate wallet load
474                 std::vector<unsigned char> vchKey;
475                 vchKey.reserve(vchPubKey.size() + pkey.size());
476                 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
477                 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
478 
479                 if (Hash(vchKey.begin(), vchKey.end()) != hash)
480                 {
481                     strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
482                     return false;
483                 }
484 
485                 fSkipCheck = true;
486             }
487 
488             if (!key.Load(pkey, vchPubKey, fSkipCheck))
489             {
490                 strErr = "Error reading wallet database: CPrivKey corrupt";
491                 return false;
492             }
493             if (!pwallet->LoadKey(key, vchPubKey))
494             {
495                 strErr = "Error reading wallet database: LoadKey failed";
496                 return false;
497             }
498         }
499         else if (strType == "mkey")
500         {
501             unsigned int nID;
502             ssKey >> nID;
503             CMasterKey kMasterKey;
504             ssValue >> kMasterKey;
505             if(pwallet->mapMasterKeys.count(nID) != 0)
506             {
507                 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
508                 return false;
509             }
510             pwallet->mapMasterKeys[nID] = kMasterKey;
511             if (pwallet->nMasterKeyMaxID < nID)
512                 pwallet->nMasterKeyMaxID = nID;
513         }
514         else if (strType == "ckey")
515         {
516             CPubKey vchPubKey;
517             ssKey >> vchPubKey;
518             if (!vchPubKey.IsValid())
519             {
520                 strErr = "Error reading wallet database: CPubKey corrupt";
521                 return false;
522             }
523             vector<unsigned char> vchPrivKey;
524             ssValue >> vchPrivKey;
525             wss.nCKeys++;
526 
527             if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
528             {
529                 strErr = "Error reading wallet database: LoadCryptedKey failed";
530                 return false;
531             }
532             wss.fIsEncrypted = true;
533         }
534         else if (strType == "keymeta")
535         {
536             CPubKey vchPubKey;
537             ssKey >> vchPubKey;
538             CKeyMetadata keyMeta;
539             ssValue >> keyMeta;
540             wss.nKeyMeta++;
541 
542             pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
543 
544             // find earliest key creation time, as wallet birthday
545             if (!pwallet->nTimeFirstKey ||
546                 (keyMeta.nCreateTime < pwallet->nTimeFirstKey))
547                 pwallet->nTimeFirstKey = keyMeta.nCreateTime;
548         }
549         else if (strType == "defaultkey")
550         {
551             ssValue >> pwallet->vchDefaultKey;
552         }
553         else if (strType == "pool")
554         {
555             int64_t nIndex;
556             ssKey >> nIndex;
557             CKeyPool keypool;
558             ssValue >> keypool;
559             pwallet->setKeyPool.insert(nIndex);
560 
561             // If no metadata exists yet, create a default with the pool key's
562             // creation time. Note that this may be overwritten by actually
563             // stored metadata for that key later, which is fine.
564             CKeyID keyid = keypool.vchPubKey.GetID();
565             if (pwallet->mapKeyMetadata.count(keyid) == 0)
566                 pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
567         }
568         else if (strType == "version")
569         {
570             ssValue >> wss.nFileVersion;
571             if (wss.nFileVersion == 10300)
572                 wss.nFileVersion = 300;
573         }
574         else if (strType == "cscript")
575         {
576             uint160 hash;
577             ssKey >> hash;
578             CScript script;
579             ssValue >> *(CScriptBase*)(&script);
580             if (!pwallet->LoadCScript(script))
581             {
582                 strErr = "Error reading wallet database: LoadCScript failed";
583                 return false;
584             }
585         }
586         else if (strType == "orderposnext")
587         {
588             ssValue >> pwallet->nOrderPosNext;
589         }
590         else if (strType == "destdata")
591         {
592             std::string strAddress, strKey, strValue;
593             ssKey >> strAddress;
594             ssKey >> strKey;
595             ssValue >> strValue;
596             if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue))
597             {
598                 strErr = "Error reading wallet database: LoadDestData failed";
599                 return false;
600             }
601         }
602         else if (strType == "hdchain")
603         {
604             CHDChain chain;
605             ssValue >> chain;
606             if (!pwallet->SetHDChain(chain, true))
607             {
608                 strErr = "Error reading wallet database: SetHDChain failed";
609                 return false;
610             }
611         }
612     } catch (...)
613     {
614         return false;
615     }
616     return true;
617 }
618 
IsKeyType(string strType)619 static bool IsKeyType(string strType)
620 {
621     return (strType== "key" || strType == "wkey" ||
622             strType == "mkey" || strType == "ckey");
623 }
624 
LoadWallet(CWallet * pwallet)625 DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
626 {
627     pwallet->vchDefaultKey = CPubKey();
628     CWalletScanState wss;
629     bool fNoncriticalErrors = false;
630     DBErrors result = DB_LOAD_OK;
631 
632     try {
633         LOCK(pwallet->cs_wallet);
634         int nMinVersion = 0;
635         if (Read((string)"minversion", nMinVersion))
636         {
637             if (nMinVersion > CLIENT_VERSION)
638                 return DB_TOO_NEW;
639             pwallet->LoadMinVersion(nMinVersion);
640         }
641 
642         // Get cursor
643         Dbc* pcursor = GetCursor();
644         if (!pcursor)
645         {
646             LogPrintf("Error getting wallet database cursor\n");
647             return DB_CORRUPT;
648         }
649 
650         while (true)
651         {
652             // Read next record
653             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
654             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
655             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
656             if (ret == DB_NOTFOUND)
657                 break;
658             else if (ret != 0)
659             {
660                 LogPrintf("Error reading next record from wallet database\n");
661                 return DB_CORRUPT;
662             }
663 
664             // Try to be tolerant of single corrupt records:
665             string strType, strErr;
666             if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
667             {
668                 // losing keys is considered a catastrophic error, anything else
669                 // we assume the user can live with:
670                 if (IsKeyType(strType))
671                     result = DB_CORRUPT;
672                 else
673                 {
674                     // Leave other errors alone, if we try to fix them we might make things worse.
675                     fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
676                     if (strType == "tx")
677                         // Rescan if there is a bad transaction record:
678                         SoftSetBoolArg("-rescan", true);
679                 }
680             }
681             if (!strErr.empty())
682                 LogPrintf("%s\n", strErr);
683         }
684         pcursor->close();
685     }
686     catch (const boost::thread_interrupted&) {
687         throw;
688     }
689     catch (...) {
690         result = DB_CORRUPT;
691     }
692 
693     if (fNoncriticalErrors && result == DB_LOAD_OK)
694         result = DB_NONCRITICAL_ERROR;
695 
696     // Any wallet corruption at all: skip any rewriting or
697     // upgrading, we don't want to make it worse.
698     if (result != DB_LOAD_OK)
699         return result;
700 
701     LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
702 
703     LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
704            wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
705 
706     // nTimeFirstKey is only reliable if all keys have metadata
707     if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
708         pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
709 
710     BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
711         WriteTx(pwallet->mapWallet[hash]);
712 
713     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
714     if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
715         return DB_NEED_REWRITE;
716 
717     if (wss.nFileVersion < CLIENT_VERSION) // Update
718         WriteVersion(CLIENT_VERSION);
719 
720     if (wss.fAnyUnordered)
721         result = ReorderTransactions(pwallet);
722 
723     pwallet->laccentries.clear();
724     ListAccountCreditDebit("*", pwallet->laccentries);
725     BOOST_FOREACH(CAccountingEntry& entry, pwallet->laccentries) {
726         pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry)));
727     }
728 
729     return result;
730 }
731 
FindWalletTx(CWallet * pwallet,vector<uint256> & vTxHash,vector<CWalletTx> & vWtx)732 DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx)
733 {
734     pwallet->vchDefaultKey = CPubKey();
735     bool fNoncriticalErrors = false;
736     DBErrors result = DB_LOAD_OK;
737 
738     try {
739         LOCK(pwallet->cs_wallet);
740         int nMinVersion = 0;
741         if (Read((string)"minversion", nMinVersion))
742         {
743             if (nMinVersion > CLIENT_VERSION)
744                 return DB_TOO_NEW;
745             pwallet->LoadMinVersion(nMinVersion);
746         }
747 
748         // Get cursor
749         Dbc* pcursor = GetCursor();
750         if (!pcursor)
751         {
752             LogPrintf("Error getting wallet database cursor\n");
753             return DB_CORRUPT;
754         }
755 
756         while (true)
757         {
758             // Read next record
759             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
760             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
761             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
762             if (ret == DB_NOTFOUND)
763                 break;
764             else if (ret != 0)
765             {
766                 LogPrintf("Error reading next record from wallet database\n");
767                 return DB_CORRUPT;
768             }
769 
770             string strType;
771             ssKey >> strType;
772             if (strType == "tx") {
773                 uint256 hash;
774                 ssKey >> hash;
775 
776                 CWalletTx wtx;
777                 ssValue >> wtx;
778 
779                 vTxHash.push_back(hash);
780                 vWtx.push_back(wtx);
781             }
782         }
783         pcursor->close();
784     }
785     catch (const boost::thread_interrupted&) {
786         throw;
787     }
788     catch (...) {
789         result = DB_CORRUPT;
790     }
791 
792     if (fNoncriticalErrors && result == DB_LOAD_OK)
793         result = DB_NONCRITICAL_ERROR;
794 
795     return result;
796 }
797 
ZapSelectTx(CWallet * pwallet,vector<uint256> & vTxHashIn,vector<uint256> & vTxHashOut)798 DBErrors CWalletDB::ZapSelectTx(CWallet* pwallet, vector<uint256>& vTxHashIn, vector<uint256>& vTxHashOut)
799 {
800     // build list of wallet TXs and hashes
801     vector<uint256> vTxHash;
802     vector<CWalletTx> vWtx;
803     DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx);
804     if (err != DB_LOAD_OK) {
805         return err;
806     }
807 
808     std::sort(vTxHash.begin(), vTxHash.end());
809     std::sort(vTxHashIn.begin(), vTxHashIn.end());
810 
811     // erase each matching wallet TX
812     bool delerror = false;
813     vector<uint256>::iterator it = vTxHashIn.begin();
814     BOOST_FOREACH (uint256 hash, vTxHash) {
815         while (it < vTxHashIn.end() && (*it) < hash) {
816             it++;
817         }
818         if (it == vTxHashIn.end()) {
819             break;
820         }
821         else if ((*it) == hash) {
822             pwallet->mapWallet.erase(hash);
823             if(!EraseTx(hash)) {
824                 LogPrint("db", "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
825                 delerror = true;
826             }
827             vTxHashOut.push_back(hash);
828         }
829     }
830 
831     if (delerror) {
832         return DB_CORRUPT;
833     }
834     return DB_LOAD_OK;
835 }
836 
ZapWalletTx(CWallet * pwallet,vector<CWalletTx> & vWtx)837 DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx)
838 {
839     // build list of wallet TXs
840     vector<uint256> vTxHash;
841     DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx);
842     if (err != DB_LOAD_OK)
843         return err;
844 
845     // erase each wallet TX
846     BOOST_FOREACH (uint256& hash, vTxHash) {
847         if (!EraseTx(hash))
848             return DB_CORRUPT;
849     }
850 
851     return DB_LOAD_OK;
852 }
853 
ThreadFlushWalletDB(const string & strFile)854 void ThreadFlushWalletDB(const string& strFile)
855 {
856     // Make this thread recognisable as the wallet flushing thread
857     RenameThread("zetacoin-wallet");
858 
859     static bool fOneThread;
860     if (fOneThread)
861         return;
862     fOneThread = true;
863     if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET))
864         return;
865 
866     unsigned int nLastSeen = nWalletDBUpdated;
867     unsigned int nLastFlushed = nWalletDBUpdated;
868     int64_t nLastWalletUpdate = GetTime();
869     while (true)
870     {
871         MilliSleep(500);
872 
873         if (nLastSeen != nWalletDBUpdated)
874         {
875             nLastSeen = nWalletDBUpdated;
876             nLastWalletUpdate = GetTime();
877         }
878 
879         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
880         {
881             TRY_LOCK(bitdb.cs_db,lockDb);
882             if (lockDb)
883             {
884                 // Don't do this if any databases are in use
885                 int nRefCount = 0;
886                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
887                 while (mi != bitdb.mapFileUseCount.end())
888                 {
889                     nRefCount += (*mi).second;
890                     mi++;
891                 }
892 
893                 if (nRefCount == 0)
894                 {
895                     boost::this_thread::interruption_point();
896                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
897                     if (mi != bitdb.mapFileUseCount.end())
898                     {
899                         LogPrint("db", "Flushing %s\n", strFile);
900                         nLastFlushed = nWalletDBUpdated;
901                         int64_t nStart = GetTimeMillis();
902 
903                         // Flush wallet file so it's self contained
904                         bitdb.CloseDb(strFile);
905                         bitdb.CheckpointLSN(strFile);
906 
907                         bitdb.mapFileUseCount.erase(mi++);
908                         LogPrint("db", "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
909                     }
910                 }
911             }
912         }
913     }
914 }
915 
916 //
917 // Try to (very carefully!) recover wallet file if there is a problem.
918 //
Recover(CDBEnv & dbenv,const std::string & filename,bool fOnlyKeys)919 bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys)
920 {
921     // Recovery procedure:
922     // move wallet file to wallet.timestamp.bak
923     // Call Salvage with fAggressive=true to
924     // get as much data as possible.
925     // Rewrite salvaged data to fresh wallet file
926     // Set -rescan so any missing transactions will be
927     // found.
928     int64_t now = GetTime();
929     std::string newFilename = strprintf("wallet.%d.bak", now);
930 
931     int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL,
932                                        newFilename.c_str(), DB_AUTO_COMMIT);
933     if (result == 0)
934         LogPrintf("Renamed %s to %s\n", filename, newFilename);
935     else
936     {
937         LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
938         return false;
939     }
940 
941     std::vector<CDBEnv::KeyValPair> salvagedData;
942     bool fSuccess = dbenv.Salvage(newFilename, true, salvagedData);
943     if (salvagedData.empty())
944     {
945         LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
946         return false;
947     }
948     LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
949 
950     boost::scoped_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0));
951     int ret = pdbCopy->open(NULL,               // Txn pointer
952                             filename.c_str(),   // Filename
953                             "main",             // Logical db name
954                             DB_BTREE,           // Database type
955                             DB_CREATE,          // Flags
956                             0);
957     if (ret > 0)
958     {
959         LogPrintf("Cannot create database file %s\n", filename);
960         return false;
961     }
962     CWallet dummyWallet;
963     CWalletScanState wss;
964 
965     DbTxn* ptxn = dbenv.TxnBegin();
966     BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
967     {
968         if (fOnlyKeys)
969         {
970             CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
971             CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
972             string strType, strErr;
973             bool fReadOK;
974             {
975                 // Required in LoadKeyMetadata():
976                 LOCK(dummyWallet.cs_wallet);
977                 fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
978                                         wss, strType, strErr);
979             }
980             if (!IsKeyType(strType) && strType != "hdchain")
981                 continue;
982             if (!fReadOK)
983             {
984                 LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
985                 continue;
986             }
987         }
988         Dbt datKey(&row.first[0], row.first.size());
989         Dbt datValue(&row.second[0], row.second.size());
990         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
991         if (ret2 > 0)
992             fSuccess = false;
993     }
994     ptxn->commit(0);
995     pdbCopy->close(0);
996 
997     return fSuccess;
998 }
999 
Recover(CDBEnv & dbenv,const std::string & filename)1000 bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename)
1001 {
1002     return CWalletDB::Recover(dbenv, filename, false);
1003 }
1004 
WriteDestData(const std::string & address,const std::string & key,const std::string & value)1005 bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
1006 {
1007     nWalletDBUpdated++;
1008     return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
1009 }
1010 
EraseDestData(const std::string & address,const std::string & key)1011 bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
1012 {
1013     nWalletDBUpdated++;
1014     return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
1015 }
1016 
1017 
WriteHDChain(const CHDChain & chain)1018 bool CWalletDB::WriteHDChain(const CHDChain& chain)
1019 {
1020     nWalletDBUpdated++;
1021     return Write(std::string("hdchain"), chain);
1022 }
1023