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