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