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