Allow new fees policy on testnet, adjust minimum fees
[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;
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             pwallet->AddToWallet(wtx, true);
256
257             //// debug print
258             //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
259             //printf(" %12"PRI64d"  %s  %s  %s\n",
260             //    wtx.vout[0].nValue,
261             //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
262             //    wtx.hashBlock.ToString().substr(0,20).c_str(),
263             //    wtx.mapValue["message"].c_str());
264         }
265         else if (strType == "acentry")
266         {
267             string strAccount;
268             ssKey >> strAccount;
269             uint64 nNumber;
270             ssKey >> nNumber;
271             if (nNumber > nAccountingEntryNumber)
272                 nAccountingEntryNumber = nNumber;
273
274             if (!wss.fAnyUnordered)
275             {
276                 CAccountingEntry acentry;
277                 ssValue >> acentry;
278                 if (acentry.nOrderPos == -1)
279                     wss.fAnyUnordered = true;
280             }
281         }
282         else if (strType == "key" || strType == "wkey")
283         {
284             vector<unsigned char> vchPubKey;
285             ssKey >> vchPubKey;
286             CKey key;
287             if (strType == "key")
288             {
289                 wss.nKeys++;
290                 CPrivKey pkey;
291                 ssValue >> pkey;
292                 key.SetPubKey(vchPubKey);
293                 if (!key.SetPrivKey(pkey))
294                 {
295                     strErr = "Error reading wallet database: CPrivKey corrupt";
296                     return false;
297                 }
298                 if (key.GetPubKey() != vchPubKey)
299                 {
300                     strErr = "Error reading wallet database: CPrivKey pubkey inconsistency";
301                     return false;
302                 }
303                 if (!key.IsValid())
304                 {
305                     strErr = "Error reading wallet database: invalid CPrivKey";
306                     return false;
307                 }
308             }
309             else
310             {
311                 CWalletKey wkey;
312                 ssValue >> wkey;
313                 key.SetPubKey(vchPubKey);
314                 if (!key.SetPrivKey(wkey.vchPrivKey))
315                 {
316                     strErr = "Error reading wallet database: CPrivKey corrupt";
317                     return false;
318                 }
319                 if (key.GetPubKey() != vchPubKey)
320                 {
321                     strErr = "Error reading wallet database: CWalletKey pubkey inconsistency";
322                     return false;
323                 }
324                 if (!key.IsValid())
325                 {
326                     strErr = "Error reading wallet database: invalid CWalletKey";
327                     return false;
328                 }
329             }
330             if (!pwallet->LoadKey(key))
331             {
332                 strErr = "Error reading wallet database: LoadKey failed";
333                 return false;
334             }
335         }
336         else if (strType == "mkey")
337         {
338             unsigned int nID;
339             ssKey >> nID;
340             CMasterKey kMasterKey;
341             ssValue >> kMasterKey;
342             if(pwallet->mapMasterKeys.count(nID) != 0)
343             {
344                 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
345                 return false;
346             }
347             pwallet->mapMasterKeys[nID] = kMasterKey;
348             if (pwallet->nMasterKeyMaxID < nID)
349                 pwallet->nMasterKeyMaxID = nID;
350         }
351         else if (strType == "ckey")
352         {
353             wss.nCKeys++;
354             vector<unsigned char> vchPubKey;
355             ssKey >> vchPubKey;
356             vector<unsigned char> vchPrivKey;
357             ssValue >> vchPrivKey;
358             if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
359             {
360                 strErr = "Error reading wallet database: LoadCryptedKey failed";
361                 return false;
362             }
363             wss.fIsEncrypted = true;
364         }
365         else if (strType == "keymeta")
366         {
367             CPubKey vchPubKey;
368             ssKey >> vchPubKey;
369             CKeyMetadata keyMeta;
370             ssValue >> keyMeta;
371             wss.nKeyMeta++;
372
373             pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
374
375             // find earliest key creation time, as wallet birthday
376             if (!pwallet->nTimeFirstKey ||
377                 (keyMeta.nCreateTime < pwallet->nTimeFirstKey))
378                 pwallet->nTimeFirstKey = keyMeta.nCreateTime;
379         }
380         else if (strType == "defaultkey")
381         {
382             ssValue >> pwallet->vchDefaultKey;
383         }
384         else if (strType == "pool")
385         {
386             int64 nIndex;
387             ssKey >> nIndex;
388             CKeyPool keypool;
389             ssValue >> keypool;
390             pwallet->setKeyPool.insert(nIndex);
391
392             // If no metadata exists yet, create a default with the pool key's
393             // creation time. Note that this may be overwritten by actually
394             // stored metadata for that key later, which is fine.
395             CKeyID keyid = keypool.vchPubKey.GetID();
396             if (pwallet->mapKeyMetadata.count(keyid) == 0)
397                 pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
398
399         }
400         else if (strType == "version")
401         {
402             ssValue >> wss.nFileVersion;
403             if (wss.nFileVersion == 10300)
404                 wss.nFileVersion = 300;
405         }
406         else if (strType == "cscript")
407         {
408             uint160 hash;
409             ssKey >> hash;
410             CScript script;
411             ssValue >> script;
412             if (!pwallet->LoadCScript(script))
413             {
414                 strErr = "Error reading wallet database: LoadCScript failed";
415                 return false;
416             }
417         }
418         else if (strType == "orderposnext")
419         {
420             ssValue >> pwallet->nOrderPosNext;
421         }
422     } catch (...)
423     {
424         return false;
425     }
426     return true;
427 }
428
429 static bool IsKeyType(string strType)
430 {
431     return (strType== "key" || strType == "wkey" ||
432             strType == "mkey" || strType == "ckey");
433 }
434
435 DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
436 {
437     pwallet->vchDefaultKey = CPubKey();
438     CWalletScanState wss;
439     bool fNoncriticalErrors = false;
440     DBErrors result = DB_LOAD_OK;
441
442     try {
443         LOCK(pwallet->cs_wallet);
444         int nMinVersion = 0;
445         if (Read((string)"minversion", nMinVersion))
446         {
447             if (nMinVersion > CLIENT_VERSION)
448                 return DB_TOO_NEW;
449             pwallet->LoadMinVersion(nMinVersion);
450         }
451
452         // Get cursor
453         Dbc* pcursor = GetCursor();
454         if (!pcursor)
455         {
456             printf("Error getting wallet database cursor\n");
457             return DB_CORRUPT;
458         }
459
460         while (true)
461         {
462             // Read next record
463             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
464             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
465             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
466             if (ret == DB_NOTFOUND)
467                 break;
468             else if (ret != 0)
469             {
470                 printf("Error reading next record from wallet database\n");
471                 return DB_CORRUPT;
472             }
473
474             // Try to be tolerant of single corrupt records:
475             string strType, strErr;
476             if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
477             {
478                 // losing keys is considered a catastrophic error, anything else
479                 // we assume the user can live with:
480                 if (IsKeyType(strType))
481                     result = DB_CORRUPT;
482                 else
483                 {
484                     // Leave other errors alone, if we try to fix them we might make things worse.
485                     fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
486                     if (strType == "tx")
487                         // Rescan if there is a bad transaction record:
488                         SoftSetBoolArg("-rescan", true);
489                 }
490             }
491             if (!strErr.empty())
492                 printf("%s\n", strErr.c_str());
493         }
494         pcursor->close();
495     }
496     catch (...)
497     {
498         result = DB_CORRUPT;
499     }
500
501     if (fNoncriticalErrors && result == DB_LOAD_OK)
502         result = DB_NONCRITICAL_ERROR;
503
504     // Any wallet corruption at all: skip any rewriting or
505     // upgrading, we don't want to make it worse.
506     if (result != DB_LOAD_OK)
507         return result;
508
509     printf("nFileVersion = %d\n", wss.nFileVersion);
510
511     printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
512            wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
513
514     // nTimeFirstKey is only reliable if all keys have metadata
515     if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
516         pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
517
518
519     BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
520         WriteTx(hash, pwallet->mapWallet[hash]);
521
522     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
523     if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
524         return DB_NEED_REWRITE;
525
526     if (wss.nFileVersion < CLIENT_VERSION) // Update
527         WriteVersion(CLIENT_VERSION);
528
529     if (wss.fAnyUnordered)
530         result = ReorderTransactions(pwallet);
531
532     return result;
533 }
534
535 DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash)
536 {
537     pwallet->vchDefaultKey = CPubKey();
538     CWalletScanState wss;
539     bool fNoncriticalErrors = false;
540     DBErrors result = DB_LOAD_OK;
541
542     try {
543         LOCK(pwallet->cs_wallet);
544         int nMinVersion = 0;
545         if (Read((string)"minversion", nMinVersion))
546         {
547             if (nMinVersion > CLIENT_VERSION)
548                 return DB_TOO_NEW;
549             pwallet->LoadMinVersion(nMinVersion);
550         }
551
552         // Get cursor
553         Dbc* pcursor = GetCursor();
554         if (!pcursor)
555         {
556             printf("Error getting wallet database cursor\n");
557             return DB_CORRUPT;
558         }
559
560         while (true)
561         {
562             // Read next record
563             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
564             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
565             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
566             if (ret == DB_NOTFOUND)
567                 break;
568             else if (ret != 0)
569             {
570                 printf("Error reading next record from wallet database\n");
571                 return DB_CORRUPT;
572             }
573
574             string strType;
575             ssKey >> strType;
576             if (strType == "tx") {
577                 uint256 hash;
578                 ssKey >> hash;
579
580                 vTxHash.push_back(hash);
581             }
582         }
583         pcursor->close();
584     }
585     catch (boost::thread_interrupted) {
586         throw;
587     }
588     catch (...) {
589         result = DB_CORRUPT;
590     }
591
592     if (fNoncriticalErrors && result == DB_LOAD_OK)
593         result = DB_NONCRITICAL_ERROR;
594
595     return result;
596 }
597
598 DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet)
599 {
600     // build list of wallet TXs
601     vector<uint256> vTxHash;
602     DBErrors err = FindWalletTx(pwallet, vTxHash);
603     if (err != DB_LOAD_OK)
604         return err;
605
606     // erase each wallet TX
607     BOOST_FOREACH (uint256& hash, vTxHash) {
608         if (!EraseTx(hash))
609             return DB_CORRUPT;
610     }
611
612     return DB_LOAD_OK;
613 }
614
615 void ThreadFlushWalletDB(void* parg)
616 {
617     // Make this thread recognisable as the wallet flushing thread
618     RenameThread("bitcoin-wallet");
619
620     const string& strFile = ((const string*)parg)[0];
621     static bool fOneThread;
622     if (fOneThread)
623         return;
624     fOneThread = true;
625     if (!GetBoolArg("-flushwallet", true))
626         return;
627
628     unsigned int nLastSeen = nWalletDBUpdated;
629     unsigned int nLastFlushed = nWalletDBUpdated;
630     int64 nLastWalletUpdate = GetTime();
631     while (!fShutdown)
632     {
633         Sleep(500);
634
635         if (nLastSeen != nWalletDBUpdated)
636         {
637             nLastSeen = nWalletDBUpdated;
638             nLastWalletUpdate = GetTime();
639         }
640
641         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
642         {
643             TRY_LOCK(bitdb.cs_db,lockDb);
644             if (lockDb)
645             {
646                 // Don't do this if any databases are in use
647                 int nRefCount = 0;
648                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
649                 while (mi != bitdb.mapFileUseCount.end())
650                 {
651                     nRefCount += (*mi).second;
652                     mi++;
653                 }
654
655                 if (nRefCount == 0 && !fShutdown)
656                 {
657                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
658                     if (mi != bitdb.mapFileUseCount.end())
659                     {
660                         printf("Flushing wallet.dat\n");
661                         nLastFlushed = nWalletDBUpdated;
662                         int64 nStart = GetTimeMillis();
663
664                         // Flush wallet.dat so it's self contained
665                         bitdb.CloseDb(strFile);
666                         bitdb.CheckpointLSN(strFile);
667
668                         bitdb.mapFileUseCount.erase(mi++);
669                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
670                     }
671                 }
672             }
673         }
674     }
675 }
676
677 bool BackupWallet(const CWallet& wallet, const string& strDest)
678 {
679     if (!wallet.fFileBacked)
680         return false;
681     while (!fShutdown)
682     {
683         {
684             LOCK(bitdb.cs_db);
685             if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
686             {
687                 // Flush log data to the dat file
688                 bitdb.CloseDb(wallet.strWalletFile);
689                 bitdb.CheckpointLSN(wallet.strWalletFile);
690                 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
691
692                 // Copy wallet.dat
693                 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
694                 filesystem::path pathDest(strDest);
695                 if (filesystem::is_directory(pathDest))
696                     pathDest /= wallet.strWalletFile;
697
698                 try {
699 #if BOOST_VERSION >= 104000
700                     filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
701 #else
702                     filesystem::copy_file(pathSrc, pathDest);
703 #endif
704                     printf("copied wallet.dat to %s\n", pathDest.string().c_str());
705                     return true;
706                 } catch(const filesystem::filesystem_error &e) {
707                     printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
708                     return false;
709                 }
710             }
711         }
712         Sleep(100);
713     }
714     return false;
715 }
716
717 //
718 // Try to (very carefully!) recover wallet.dat if there is a problem.
719 //
720 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
721 {
722     // Recovery procedure:
723     // move wallet.dat to wallet.timestamp.bak
724     // Call Salvage with fAggressive=true to
725     // get as much data as possible.
726     // Rewrite salvaged data to wallet.dat
727     // Set -rescan so any missing transactions will be
728     // found.
729     int64 now = GetTime();
730     std::string newFilename = strprintf("wallet.%"PRI64d".bak", now);
731
732     int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
733                                       newFilename.c_str(), DB_AUTO_COMMIT);
734     if (result == 0)
735         printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
736     else
737     {
738         printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
739         return false;
740     }
741
742     std::vector<CDBEnv::KeyValPair> salvagedData;
743     bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
744     if (salvagedData.empty())
745     {
746         printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
747         return false;
748     }
749     printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size());
750
751     bool fSuccess = allOK;
752     Db* pdbCopy = new Db(&dbenv.dbenv, 0);
753     int ret = pdbCopy->open(NULL,                 // Txn pointer
754                             filename.c_str(),   // Filename
755                             "main",    // Logical db name
756                             DB_BTREE,  // Database type
757                             DB_CREATE,    // Flags
758                             0);
759     if (ret > 0)
760     {
761         printf("Cannot create database file %s\n", filename.c_str());
762         return false;
763     }
764     CWallet dummyWallet;
765     CWalletScanState wss;
766
767     DbTxn* ptxn = dbenv.TxnBegin();
768     BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
769     {
770         if (fOnlyKeys)
771         {
772             CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
773             CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
774             string strType, strErr;
775             bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
776                                         wss, strType, strErr);
777             if (!IsKeyType(strType))
778                 continue;
779             if (!fReadOK)
780             {
781                 printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
782                 continue;
783             }
784         }
785         Dbt datKey(&row.first[0], row.first.size());
786         Dbt datValue(&row.second[0], row.second.size());
787         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
788         if (ret2 > 0)
789             fSuccess = false;
790     }
791     ptxn->commit(0);
792     pdbCopy->close(0);
793     delete pdbCopy;
794
795     return fSuccess;
796 }
797
798 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
799 {
800     return CWalletDB::Recover(dbenv, filename, false);
801 }