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