43730c1776c370ad1a55949089ea5a18340e74b4
[novacoin.git] / src / walletdb.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "walletdb.h"
7 #include "wallet.h"
8 #include <boost/version.hpp>
9 #include <boost/filesystem.hpp>
10
11 using namespace std;
12 using namespace boost;
13
14
15 static uint64 nAccountingEntryNumber = 0;
16 extern bool fWalletUnlockMintOnly;
17
18 //
19 // CWalletDB
20 //
21
22 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
23 {
24     nWalletDBUpdated++;
25     return Write(make_pair(string("name"), strAddress), strName);
26 }
27
28 bool CWalletDB::EraseName(const string& strAddress)
29 {
30     // This should only be used for sending addresses, never for receiving addresses,
31     // receiving addresses must always have an address book entry if they're not change return.
32     nWalletDBUpdated++;
33     return Erase(make_pair(string("name"), strAddress));
34 }
35
36 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
37 {
38     account.SetNull();
39     return Read(make_pair(string("acc"), strAccount), account);
40 }
41
42 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
43 {
44     return Write(make_pair(string("acc"), strAccount), account);
45 }
46
47 bool CWalletDB::WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry)
48 {
49     return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry);
50 }
51
52 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
53 {
54     return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
55 }
56
57 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
58 {
59     list<CAccountingEntry> entries;
60     ListAccountCreditDebit(strAccount, entries);
61
62     int64 nCreditDebit = 0;
63     BOOST_FOREACH (const CAccountingEntry& entry, entries)
64         nCreditDebit += entry.nCreditDebit;
65
66     return nCreditDebit;
67 }
68
69 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
70 {
71     bool fAllAccounts = (strAccount == "*");
72
73     Dbc* pcursor = GetCursor();
74     if (!pcursor)
75         throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
76     unsigned int fFlags = DB_SET_RANGE;
77     while (true)
78     {
79         // Read next record
80         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
81         if (fFlags == DB_SET_RANGE)
82             ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
83         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
84         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
85         fFlags = DB_NEXT;
86         if (ret == DB_NOTFOUND)
87             break;
88         else if (ret != 0)
89         {
90             pcursor->close();
91             throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
92         }
93
94         // Unserialize
95         string strType;
96         ssKey >> strType;
97         if (strType != "acentry")
98             break;
99         CAccountingEntry acentry;
100         ssKey >> acentry.strAccount;
101         if (!fAllAccounts && acentry.strAccount != strAccount)
102             break;
103
104         ssValue >> acentry;
105         ssKey >> acentry.nEntryNo;
106         entries.push_back(acentry);
107     }
108
109     pcursor->close();
110 }
111
112
113 DBErrors
114 CWalletDB::ReorderTransactions(CWallet* pwallet)
115 {
116     LOCK(pwallet->cs_wallet);
117     // Old wallets didn't have any defined order for transactions
118     // Probably a bad idea to change the output of this
119
120     // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
121     typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
122     typedef multimap<int64, TxPair > TxItems;
123     TxItems txByTime;
124
125     for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
126     {
127         CWalletTx* wtx = &((*it).second);
128         txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
129     }
130     list<CAccountingEntry> acentries;
131     ListAccountCreditDebit("", acentries);
132     BOOST_FOREACH(CAccountingEntry& entry, acentries)
133     {
134         txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
135     }
136
137     int64& nOrderPosNext = pwallet->nOrderPosNext;
138     nOrderPosNext = 0;
139     std::vector<int64> nOrderPosOffsets;
140     for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
141     {
142         CWalletTx *const pwtx = (*it).second.first;
143         CAccountingEntry *const pacentry = (*it).second.second;
144         int64& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
145
146         if (nOrderPos == -1)
147         {
148             nOrderPos = nOrderPosNext++;
149             nOrderPosOffsets.push_back(nOrderPos);
150
151             if (pacentry)
152                 // Have to write accounting regardless, since we don't keep it in memory
153                 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
154                     return DB_LOAD_FAIL;
155         }
156         else
157         {
158             int64 nOrderPosOff = 0;
159             BOOST_FOREACH(const int64& nOffsetStart, nOrderPosOffsets)
160             {
161                 if (nOrderPos >= nOffsetStart)
162                     ++nOrderPosOff;
163             }
164             nOrderPos += nOrderPosOff;
165             nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
166
167             if (!nOrderPosOff)
168                 continue;
169
170             // Since we're changing the order, write it back
171             if (pwtx)
172             {
173                 if (!WriteTx(pwtx->GetHash(), *pwtx))
174                     return DB_LOAD_FAIL;
175             }
176             else
177                 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
178                     return DB_LOAD_FAIL;
179         }
180     }
181
182     return DB_LOAD_OK;
183 }
184
185 class CWalletScanState {
186 public:
187     unsigned int nKeys;
188     unsigned int nCKeys;
189     unsigned int nKeyMeta;
190     bool fIsEncrypted;
191     bool fAnyUnordered;
192     int nFileVersion;
193     vector<uint256> vWalletUpgrade;
194
195     CWalletScanState() {
196         nKeys = nCKeys = nKeyMeta = 0;
197         fIsEncrypted = false;
198         fAnyUnordered = false;
199         nFileVersion = 0;
200     }
201 };
202
203 bool
204 ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
205              CWalletScanState &wss, string& strType, string& strErr)
206 {
207     try {
208         // Unserialize
209         // Taking advantage of the fact that pair serialization
210         // is just the two items serialized one after the other
211         ssKey >> strType;
212         if (strType == "name")
213         {
214             string strAddress;
215             ssKey >> strAddress;
216             ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()];
217         }
218         else if (strType == "tx")
219         {
220             uint256 hash;
221             ssKey >> hash;
222             CWalletTx& wtx = pwallet->mapWallet[hash];
223             ssValue >> wtx;
224             if (wtx.CheckTransaction() && (wtx.GetHash() == hash))
225                 wtx.BindWallet(pwallet);
226             else
227             {
228                 pwallet->mapWallet.erase(hash);
229                 return false;
230             }
231
232             // Undo serialize changes in 31600
233             if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
234             {
235                 if (!ssValue.empty())
236                 {
237                     char fTmp;
238                     char fUnused;
239                     ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
240                     strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
241                                        wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
242                     wtx.fTimeReceivedIsTxTime = fTmp;
243                 }
244                 else
245                 {
246                     strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
247                     wtx.fTimeReceivedIsTxTime = 0;
248                 }
249                 wss.vWalletUpgrade.push_back(hash);
250             }
251
252             if (wtx.nOrderPos == -1)
253                 wss.fAnyUnordered = true;
254
255             //// debug print
256             //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
257             //printf(" %12"PRI64d"  %s  %s  %s\n",
258             //    wtx.vout[0].nValue,
259             //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
260             //    wtx.hashBlock.ToString().substr(0,20).c_str(),
261             //    wtx.mapValue["message"].c_str());
262         }
263         else if (strType == "acentry")
264         {
265             string strAccount;
266             ssKey >> strAccount;
267             uint64 nNumber;
268             ssKey >> nNumber;
269             if (nNumber > nAccountingEntryNumber)
270                 nAccountingEntryNumber = nNumber;
271
272             if (!wss.fAnyUnordered)
273             {
274                 CAccountingEntry acentry;
275                 ssValue >> acentry;
276                 if (acentry.nOrderPos == -1)
277                     wss.fAnyUnordered = true;
278             }
279         }
280         else if (strType == "key" || strType == "wkey")
281         {
282             vector<unsigned char> vchPubKey;
283             ssKey >> vchPubKey;
284             CKey key;
285             if (strType == "key")
286             {
287                 wss.nKeys++;
288                 CPrivKey pkey;
289                 ssValue >> pkey;
290                 key.SetPubKey(vchPubKey);
291                 if (!key.SetPrivKey(pkey))
292                 {
293                     strErr = "Error reading wallet database: CPrivKey corrupt";
294                     return false;
295                 }
296                 if (key.GetPubKey() != vchPubKey)
297                 {
298                     strErr = "Error reading wallet database: CPrivKey pubkey inconsistency";
299                     return false;
300                 }
301                 if (!key.IsValid())
302                 {
303                     strErr = "Error reading wallet database: invalid CPrivKey";
304                     return false;
305                 }
306             }
307             else
308             {
309                 CWalletKey wkey;
310                 ssValue >> wkey;
311                 key.SetPubKey(vchPubKey);
312                 if (!key.SetPrivKey(wkey.vchPrivKey))
313                 {
314                     strErr = "Error reading wallet database: CPrivKey corrupt";
315                     return false;
316                 }
317                 if (key.GetPubKey() != vchPubKey)
318                 {
319                     strErr = "Error reading wallet database: CWalletKey pubkey inconsistency";
320                     return false;
321                 }
322                 if (!key.IsValid())
323                 {
324                     strErr = "Error reading wallet database: invalid CWalletKey";
325                     return false;
326                 }
327             }
328             if (!pwallet->LoadKey(key))
329             {
330                 strErr = "Error reading wallet database: LoadKey failed";
331                 return false;
332             }
333         }
334         else if (strType == "mkey")
335         {
336             unsigned int nID;
337             ssKey >> nID;
338             CMasterKey kMasterKey;
339             ssValue >> kMasterKey;
340             if(pwallet->mapMasterKeys.count(nID) != 0)
341             {
342                 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
343                 return false;
344             }
345             pwallet->mapMasterKeys[nID] = kMasterKey;
346             if (pwallet->nMasterKeyMaxID < nID)
347                 pwallet->nMasterKeyMaxID = nID;
348         }
349         else if (strType == "ckey")
350         {
351             wss.nCKeys++;
352             vector<unsigned char> vchPubKey;
353             ssKey >> vchPubKey;
354             vector<unsigned char> vchPrivKey;
355             ssValue >> vchPrivKey;
356             if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
357             {
358                 strErr = "Error reading wallet database: LoadCryptedKey failed";
359                 return false;
360             }
361             wss.fIsEncrypted = true;
362         }
363         else if (strType == "keymeta")
364         {
365             CPubKey vchPubKey;
366             ssKey >> vchPubKey;
367             CKeyMetadata keyMeta;
368             ssValue >> keyMeta;
369             wss.nKeyMeta++;
370
371             pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
372
373             // find earliest key creation time, as wallet birthday
374             if (!pwallet->nTimeFirstKey ||
375                 (keyMeta.nCreateTime < pwallet->nTimeFirstKey))
376                 pwallet->nTimeFirstKey = keyMeta.nCreateTime;
377         }
378         else if (strType == "defaultkey")
379         {
380             ssValue >> pwallet->vchDefaultKey;
381         }
382         else if (strType == "pool")
383         {
384             int64 nIndex;
385             ssKey >> nIndex;
386             CKeyPool keypool;
387             ssValue >> keypool;
388             pwallet->setKeyPool.insert(nIndex);
389
390             // If no metadata exists yet, create a default with the pool key's
391             // creation time. Note that this may be overwritten by actually
392             // stored metadata for that key later, which is fine.
393             CKeyID keyid = keypool.vchPubKey.GetID();
394             if (pwallet->mapKeyMetadata.count(keyid) == 0)
395                 pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
396
397         }
398         else if (strType == "version")
399         {
400             ssValue >> wss.nFileVersion;
401             if (wss.nFileVersion == 10300)
402                 wss.nFileVersion = 300;
403         }
404         else if (strType == "cscript")
405         {
406             uint160 hash;
407             ssKey >> hash;
408             CScript script;
409             ssValue >> script;
410             if (!pwallet->LoadCScript(script))
411             {
412                 strErr = "Error reading wallet database: LoadCScript failed";
413                 return false;
414             }
415         }
416         else if (strType == "orderposnext")
417         {
418             ssValue >> pwallet->nOrderPosNext;
419         }
420     } catch (...)
421     {
422         return false;
423     }
424     return true;
425 }
426
427 static bool IsKeyType(string strType)
428 {
429     return (strType== "key" || strType == "wkey" ||
430             strType == "mkey" || strType == "ckey");
431 }
432
433 DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
434 {
435     pwallet->vchDefaultKey = CPubKey();
436     CWalletScanState wss;
437     bool fNoncriticalErrors = false;
438     DBErrors result = DB_LOAD_OK;
439
440     try {
441         LOCK(pwallet->cs_wallet);
442         int nMinVersion = 0;
443         if (Read((string)"minversion", nMinVersion))
444         {
445             if (nMinVersion > CLIENT_VERSION)
446                 return DB_TOO_NEW;
447             pwallet->LoadMinVersion(nMinVersion);
448         }
449
450         // Get cursor
451         Dbc* pcursor = GetCursor();
452         if (!pcursor)
453         {
454             printf("Error getting wallet database cursor\n");
455             return DB_CORRUPT;
456         }
457
458         while (true)
459         {
460             // Read next record
461             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
462             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
463             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
464             if (ret == DB_NOTFOUND)
465                 break;
466             else if (ret != 0)
467             {
468                 printf("Error reading next record from wallet database\n");
469                 return DB_CORRUPT;
470             }
471
472             // Try to be tolerant of single corrupt records:
473             string strType, strErr;
474             if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
475             {
476                 // losing keys is considered a catastrophic error, anything else
477                 // we assume the user can live with:
478                 if (IsKeyType(strType))
479                     result = DB_CORRUPT;
480                 else
481                 {
482                     // Leave other errors alone, if we try to fix them we might make things worse.
483                     fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
484                     if (strType == "tx")
485                         // Rescan if there is a bad transaction record:
486                         SoftSetBoolArg("-rescan", true);
487                 }
488             }
489             if (!strErr.empty())
490                 printf("%s\n", strErr.c_str());
491         }
492         pcursor->close();
493     }
494     catch (...)
495     {
496         result = DB_CORRUPT;
497     }
498
499     if (fNoncriticalErrors && result == DB_LOAD_OK)
500         result = DB_NONCRITICAL_ERROR;
501
502     // Any wallet corruption at all: skip any rewriting or
503     // upgrading, we don't want to make it worse.
504     if (result != DB_LOAD_OK)
505         return result;
506
507     printf("nFileVersion = %d\n", wss.nFileVersion);
508
509     printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
510            wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
511
512     // nTimeFirstKey is only reliable if all keys have metadata
513     if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
514         pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
515
516
517     BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
518         WriteTx(hash, pwallet->mapWallet[hash]);
519
520     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
521     if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
522         return DB_NEED_REWRITE;
523
524     if (wss.nFileVersion < CLIENT_VERSION) // Update
525         WriteVersion(CLIENT_VERSION);
526
527     if (wss.fAnyUnordered)
528         result = ReorderTransactions(pwallet);
529
530     return result;
531 }
532
533 DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash)
534 {
535     pwallet->vchDefaultKey = CPubKey();
536     CWalletScanState wss;
537     bool fNoncriticalErrors = false;
538     DBErrors result = DB_LOAD_OK;
539
540     try {
541         LOCK(pwallet->cs_wallet);
542         int nMinVersion = 0;
543         if (Read((string)"minversion", nMinVersion))
544         {
545             if (nMinVersion > CLIENT_VERSION)
546                 return DB_TOO_NEW;
547             pwallet->LoadMinVersion(nMinVersion);
548         }
549
550         // Get cursor
551         Dbc* pcursor = GetCursor();
552         if (!pcursor)
553         {
554             printf("Error getting wallet database cursor\n");
555             return DB_CORRUPT;
556         }
557
558         while (true)
559         {
560             // Read next record
561             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
562             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
563             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
564             if (ret == DB_NOTFOUND)
565                 break;
566             else if (ret != 0)
567             {
568                 printf("Error reading next record from wallet database\n");
569                 return DB_CORRUPT;
570             }
571
572             string strType;
573             ssKey >> strType;
574             if (strType == "tx") {
575                 uint256 hash;
576                 ssKey >> hash;
577
578                 vTxHash.push_back(hash);
579             }
580         }
581         pcursor->close();
582     }
583     catch (boost::thread_interrupted) {
584         throw;
585     }
586     catch (...) {
587         result = DB_CORRUPT;
588     }
589
590     if (fNoncriticalErrors && result == DB_LOAD_OK)
591         result = DB_NONCRITICAL_ERROR;
592
593     return result;
594 }
595
596 DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet)
597 {
598     // build list of wallet TXs
599     vector<uint256> vTxHash;
600     DBErrors err = FindWalletTx(pwallet, vTxHash);
601     if (err != DB_LOAD_OK)
602         return err;
603
604     // erase each wallet TX
605     BOOST_FOREACH (uint256& hash, vTxHash) {
606         if (!EraseTx(hash))
607             return DB_CORRUPT;
608     }
609
610     return DB_LOAD_OK;
611 }
612
613 void ThreadFlushWalletDB(void* parg)
614 {
615     // Make this thread recognisable as the wallet flushing thread
616     RenameThread("bitcoin-wallet");
617
618     const string& strFile = ((const string*)parg)[0];
619     static bool fOneThread;
620     if (fOneThread)
621         return;
622     fOneThread = true;
623     if (!GetBoolArg("-flushwallet", true))
624         return;
625
626     unsigned int nLastSeen = nWalletDBUpdated;
627     unsigned int nLastFlushed = nWalletDBUpdated;
628     int64 nLastWalletUpdate = GetTime();
629     while (!fShutdown)
630     {
631         Sleep(500);
632
633         if (nLastSeen != nWalletDBUpdated)
634         {
635             nLastSeen = nWalletDBUpdated;
636             nLastWalletUpdate = GetTime();
637         }
638
639         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
640         {
641             TRY_LOCK(bitdb.cs_db,lockDb);
642             if (lockDb)
643             {
644                 // Don't do this if any databases are in use
645                 int nRefCount = 0;
646                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
647                 while (mi != bitdb.mapFileUseCount.end())
648                 {
649                     nRefCount += (*mi).second;
650                     mi++;
651                 }
652
653                 if (nRefCount == 0 && !fShutdown)
654                 {
655                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
656                     if (mi != bitdb.mapFileUseCount.end())
657                     {
658                         printf("Flushing wallet.dat\n");
659                         nLastFlushed = nWalletDBUpdated;
660                         int64 nStart = GetTimeMillis();
661
662                         // Flush wallet.dat so it's self contained
663                         bitdb.CloseDb(strFile);
664                         bitdb.CheckpointLSN(strFile);
665
666                         bitdb.mapFileUseCount.erase(mi++);
667                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
668                     }
669                 }
670             }
671         }
672     }
673 }
674
675 bool BackupWallet(const CWallet& wallet, const string& strDest)
676 {
677     if (!wallet.fFileBacked)
678         return false;
679     while (!fShutdown)
680     {
681         {
682             LOCK(bitdb.cs_db);
683             if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
684             {
685                 // Flush log data to the dat file
686                 bitdb.CloseDb(wallet.strWalletFile);
687                 bitdb.CheckpointLSN(wallet.strWalletFile);
688                 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
689
690                 // Copy wallet.dat
691                 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
692                 filesystem::path pathDest(strDest);
693                 if (filesystem::is_directory(pathDest))
694                     pathDest /= wallet.strWalletFile;
695
696                 try {
697 #if BOOST_VERSION >= 104000
698                     filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
699 #else
700                     filesystem::copy_file(pathSrc, pathDest);
701 #endif
702                     printf("copied wallet.dat to %s\n", pathDest.string().c_str());
703                     return true;
704                 } catch(const filesystem::filesystem_error &e) {
705                     printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
706                     return false;
707                 }
708             }
709         }
710         Sleep(100);
711     }
712     return false;
713 }
714
715 //
716 // Try to (very carefully!) recover wallet.dat if there is a problem.
717 //
718 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
719 {
720     // Recovery procedure:
721     // move wallet.dat to wallet.timestamp.bak
722     // Call Salvage with fAggressive=true to
723     // get as much data as possible.
724     // Rewrite salvaged data to wallet.dat
725     // Set -rescan so any missing transactions will be
726     // found.
727     int64 now = GetTime();
728     std::string newFilename = strprintf("wallet.%"PRI64d".bak", now);
729
730     int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
731                                       newFilename.c_str(), DB_AUTO_COMMIT);
732     if (result == 0)
733         printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
734     else
735     {
736         printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
737         return false;
738     }
739
740     std::vector<CDBEnv::KeyValPair> salvagedData;
741     bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
742     if (salvagedData.empty())
743     {
744         printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
745         return false;
746     }
747     printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size());
748
749     bool fSuccess = allOK;
750     Db* pdbCopy = new Db(&dbenv.dbenv, 0);
751     int ret = pdbCopy->open(NULL,                 // Txn pointer
752                             filename.c_str(),   // Filename
753                             "main",    // Logical db name
754                             DB_BTREE,  // Database type
755                             DB_CREATE,    // Flags
756                             0);
757     if (ret > 0)
758     {
759         printf("Cannot create database file %s\n", filename.c_str());
760         return false;
761     }
762     CWallet dummyWallet;
763     CWalletScanState wss;
764
765     DbTxn* ptxn = dbenv.TxnBegin();
766     BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
767     {
768         if (fOnlyKeys)
769         {
770             CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
771             CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
772             string strType, strErr;
773             bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
774                                         wss, strType, strErr);
775             if (!IsKeyType(strType))
776                 continue;
777             if (!fReadOK)
778             {
779                 printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
780                 continue;
781             }
782         }
783         Dbt datKey(&row.first[0], row.first.size());
784         Dbt datValue(&row.second[0], row.second.size());
785         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
786         if (ret2 > 0)
787             fSuccess = false;
788     }
789     ptxn->commit(0);
790     pdbCopy->close(0);
791     delete pdbCopy;
792
793     return fSuccess;
794 }
795
796 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
797 {
798     return CWalletDB::Recover(dbenv, filename, false);
799 }