BUGFIX: Move boost/version.hpp include from db.cpp to walletdb.cpp where BOOST_VERSIO...
[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     loop
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         loop
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 void ThreadFlushWalletDB(void* parg)
534 {
535     // Make this thread recognisable as the wallet flushing thread
536     RenameThread("bitcoin-wallet");
537
538     const string& strFile = ((const string*)parg)[0];
539     static bool fOneThread;
540     if (fOneThread)
541         return;
542     fOneThread = true;
543     if (!GetBoolArg("-flushwallet", true))
544         return;
545
546     unsigned int nLastSeen = nWalletDBUpdated;
547     unsigned int nLastFlushed = nWalletDBUpdated;
548     int64 nLastWalletUpdate = GetTime();
549     while (!fShutdown)
550     {
551         Sleep(500);
552
553         if (nLastSeen != nWalletDBUpdated)
554         {
555             nLastSeen = nWalletDBUpdated;
556             nLastWalletUpdate = GetTime();
557         }
558
559         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
560         {
561             TRY_LOCK(bitdb.cs_db,lockDb);
562             if (lockDb)
563             {
564                 // Don't do this if any databases are in use
565                 int nRefCount = 0;
566                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
567                 while (mi != bitdb.mapFileUseCount.end())
568                 {
569                     nRefCount += (*mi).second;
570                     mi++;
571                 }
572
573                 if (nRefCount == 0 && !fShutdown)
574                 {
575                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
576                     if (mi != bitdb.mapFileUseCount.end())
577                     {
578                         printf("Flushing wallet.dat\n");
579                         nLastFlushed = nWalletDBUpdated;
580                         int64 nStart = GetTimeMillis();
581
582                         // Flush wallet.dat so it's self contained
583                         bitdb.CloseDb(strFile);
584                         bitdb.CheckpointLSN(strFile);
585
586                         bitdb.mapFileUseCount.erase(mi++);
587                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
588                     }
589                 }
590             }
591         }
592     }
593 }
594
595 bool BackupWallet(const CWallet& wallet, const string& strDest)
596 {
597     if (!wallet.fFileBacked)
598         return false;
599     while (!fShutdown)
600     {
601         {
602             LOCK(bitdb.cs_db);
603             if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
604             {
605                 // Flush log data to the dat file
606                 bitdb.CloseDb(wallet.strWalletFile);
607                 bitdb.CheckpointLSN(wallet.strWalletFile);
608                 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
609
610                 // Copy wallet.dat
611                 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
612                 filesystem::path pathDest(strDest);
613                 if (filesystem::is_directory(pathDest))
614                     pathDest /= wallet.strWalletFile;
615
616                 try {
617 #if BOOST_VERSION >= 104000
618                     filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
619 #else
620                     filesystem::copy_file(pathSrc, pathDest);
621 #endif
622                     printf("copied wallet.dat to %s\n", pathDest.string().c_str());
623                     return true;
624                 } catch(const filesystem::filesystem_error &e) {
625                     printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
626                     return false;
627                 }
628             }
629         }
630         Sleep(100);
631     }
632     return false;
633 }
634
635 //
636 // Try to (very carefully!) recover wallet.dat if there is a problem.
637 //
638 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
639 {
640     // Recovery procedure:
641     // move wallet.dat to wallet.timestamp.bak
642     // Call Salvage with fAggressive=true to
643     // get as much data as possible.
644     // Rewrite salvaged data to wallet.dat
645     // Set -rescan so any missing transactions will be
646     // found.
647     int64 now = GetTime();
648     std::string newFilename = strprintf("wallet.%"PRI64d".bak", now);
649
650     int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
651                                       newFilename.c_str(), DB_AUTO_COMMIT);
652     if (result == 0)
653         printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
654     else
655     {
656         printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
657         return false;
658     }
659
660     std::vector<CDBEnv::KeyValPair> salvagedData;
661     bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
662     if (salvagedData.empty())
663     {
664         printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
665         return false;
666     }
667     printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size());
668
669     bool fSuccess = allOK;
670     Db* pdbCopy = new Db(&dbenv.dbenv, 0);
671     int ret = pdbCopy->open(NULL,                 // Txn pointer
672                             filename.c_str(),   // Filename
673                             "main",    // Logical db name
674                             DB_BTREE,  // Database type
675                             DB_CREATE,    // Flags
676                             0);
677     if (ret > 0)
678     {
679         printf("Cannot create database file %s\n", filename.c_str());
680         return false;
681     }
682     CWallet dummyWallet;
683     CWalletScanState wss;
684
685     DbTxn* ptxn = dbenv.TxnBegin();
686     BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
687     {
688         if (fOnlyKeys)
689         {
690             CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
691             CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
692             string strType, strErr;
693             bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
694                                         wss, strType, strErr);
695             if (!IsKeyType(strType))
696                 continue;
697             if (!fReadOK)
698             {
699                 printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
700                 continue;
701             }
702         }
703         Dbt datKey(&row.first[0], row.first.size());
704         Dbt datValue(&row.second[0], row.second.size());
705         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
706         if (ret2 > 0)
707             fSuccess = false;
708     }
709     ptxn->commit(0);
710     pdbCopy->close(0);
711     delete pdbCopy;
712
713     return fSuccess;
714 }
715
716 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
717 {
718     return CWalletDB::Recover(dbenv, filename, false);
719 }