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