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