]> Git Repo - VerusCoin.git/blob - src/walletdb.cpp
Remove dummy PRIszX macros for formatting
[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                 return false;
357
358             // Undo serialize changes in 31600
359             if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
360             {
361                 if (!ssValue.empty())
362                 {
363                     char fTmp;
364                     char fUnused;
365                     ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
366                     strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
367                                        wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString());
368                     wtx.fTimeReceivedIsTxTime = fTmp;
369                 }
370                 else
371                 {
372                     strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
373                     wtx.fTimeReceivedIsTxTime = 0;
374                 }
375                 wss.vWalletUpgrade.push_back(hash);
376             }
377
378             if (wtx.nOrderPos == -1)
379                 wss.fAnyUnordered = true;
380
381             pwallet->AddToWallet(wtx, true);
382             //// debug print
383             //LogPrintf("LoadWallet  %s\n", wtx.GetHash().ToString());
384             //LogPrintf(" %12d  %s  %s  %s\n",
385             //    wtx.vout[0].nValue,
386             //    DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()),
387             //    wtx.hashBlock.ToString(),
388             //    wtx.mapValue["message"]);
389         }
390         else if (strType == "acentry")
391         {
392             string strAccount;
393             ssKey >> strAccount;
394             uint64_t nNumber;
395             ssKey >> nNumber;
396             if (nNumber > nAccountingEntryNumber)
397                 nAccountingEntryNumber = nNumber;
398
399             if (!wss.fAnyUnordered)
400             {
401                 CAccountingEntry acentry;
402                 ssValue >> acentry;
403                 if (acentry.nOrderPos == -1)
404                     wss.fAnyUnordered = true;
405             }
406         }
407         else if (strType == "key" || strType == "wkey")
408         {
409             CPubKey vchPubKey;
410             ssKey >> vchPubKey;
411             if (!vchPubKey.IsValid())
412             {
413                 strErr = "Error reading wallet database: CPubKey corrupt";
414                 return false;
415             }
416             CKey key;
417             CPrivKey pkey;
418             uint256 hash = 0;
419
420             if (strType == "key")
421             {
422                 wss.nKeys++;
423                 ssValue >> pkey;
424             } else {
425                 CWalletKey wkey;
426                 ssValue >> wkey;
427                 pkey = wkey.vchPrivKey;
428             }
429
430             // Old wallets store keys as "key" [pubkey] => [privkey]
431             // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
432             // using EC operations as a checksum.
433             // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
434             // remaining backwards-compatible.
435             try
436             {
437                 ssValue >> hash;
438             }
439             catch(...){}
440
441             bool fSkipCheck = false;
442
443             if (hash != 0)
444             {
445                 // hash pubkey/privkey to accelerate wallet load
446                 std::vector<unsigned char> vchKey;
447                 vchKey.reserve(vchPubKey.size() + pkey.size());
448                 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
449                 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
450
451                 if (Hash(vchKey.begin(), vchKey.end()) != hash)
452                 {
453                     strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
454                     return false;
455                 }
456
457                 fSkipCheck = true;
458             }
459
460             if (!key.Load(pkey, vchPubKey, fSkipCheck))
461             {
462                 strErr = "Error reading wallet database: CPrivKey corrupt";
463                 return false;
464             }
465             if (!pwallet->LoadKey(key, vchPubKey))
466             {
467                 strErr = "Error reading wallet database: LoadKey failed";
468                 return false;
469             }
470         }
471         else if (strType == "mkey")
472         {
473             unsigned int nID;
474             ssKey >> nID;
475             CMasterKey kMasterKey;
476             ssValue >> kMasterKey;
477             if(pwallet->mapMasterKeys.count(nID) != 0)
478             {
479                 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
480                 return false;
481             }
482             pwallet->mapMasterKeys[nID] = kMasterKey;
483             if (pwallet->nMasterKeyMaxID < nID)
484                 pwallet->nMasterKeyMaxID = nID;
485         }
486         else if (strType == "ckey")
487         {
488             vector<unsigned char> vchPubKey;
489             ssKey >> vchPubKey;
490             vector<unsigned char> vchPrivKey;
491             ssValue >> vchPrivKey;
492             wss.nCKeys++;
493
494             if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
495             {
496                 strErr = "Error reading wallet database: LoadCryptedKey failed";
497                 return false;
498             }
499             wss.fIsEncrypted = true;
500         }
501         else if (strType == "keymeta")
502         {
503             CPubKey vchPubKey;
504             ssKey >> vchPubKey;
505             CKeyMetadata keyMeta;
506             ssValue >> keyMeta;
507             wss.nKeyMeta++;
508
509             pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
510
511             // find earliest key creation time, as wallet birthday
512             if (!pwallet->nTimeFirstKey ||
513                 (keyMeta.nCreateTime < pwallet->nTimeFirstKey))
514                 pwallet->nTimeFirstKey = keyMeta.nCreateTime;
515         }
516         else if (strType == "defaultkey")
517         {
518             ssValue >> pwallet->vchDefaultKey;
519         }
520         else if (strType == "pool")
521         {
522             int64_t nIndex;
523             ssKey >> nIndex;
524             CKeyPool keypool;
525             ssValue >> keypool;
526             pwallet->setKeyPool.insert(nIndex);
527
528             // If no metadata exists yet, create a default with the pool key's
529             // creation time. Note that this may be overwritten by actually
530             // stored metadata for that key later, which is fine.
531             CKeyID keyid = keypool.vchPubKey.GetID();
532             if (pwallet->mapKeyMetadata.count(keyid) == 0)
533                 pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
534         }
535         else if (strType == "version")
536         {
537             ssValue >> wss.nFileVersion;
538             if (wss.nFileVersion == 10300)
539                 wss.nFileVersion = 300;
540         }
541         else if (strType == "cscript")
542         {
543             uint160 hash;
544             ssKey >> hash;
545             CScript script;
546             ssValue >> script;
547             if (!pwallet->LoadCScript(script))
548             {
549                 strErr = "Error reading wallet database: LoadCScript failed";
550                 return false;
551             }
552         }
553         else if (strType == "orderposnext")
554         {
555             ssValue >> pwallet->nOrderPosNext;
556         }
557         else if (strType == "destdata")
558         {
559             std::string strAddress, strKey, strValue;
560             ssKey >> strAddress;
561             ssKey >> strKey;
562             ssValue >> strValue;
563             if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue))
564             {
565                 strErr = "Error reading wallet database: LoadDestData failed";
566                 return false;
567             }
568         }
569     } catch (...)
570     {
571         return false;
572     }
573     return true;
574 }
575
576 static bool IsKeyType(string strType)
577 {
578     return (strType== "key" || strType == "wkey" ||
579             strType == "mkey" || strType == "ckey");
580 }
581
582 DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
583 {
584     pwallet->vchDefaultKey = CPubKey();
585     CWalletScanState wss;
586     bool fNoncriticalErrors = false;
587     DBErrors result = DB_LOAD_OK;
588
589     try {
590         LOCK(pwallet->cs_wallet);
591         int nMinVersion = 0;
592         if (Read((string)"minversion", nMinVersion))
593         {
594             if (nMinVersion > CLIENT_VERSION)
595                 return DB_TOO_NEW;
596             pwallet->LoadMinVersion(nMinVersion);
597         }
598
599         // Get cursor
600         Dbc* pcursor = GetCursor();
601         if (!pcursor)
602         {
603             LogPrintf("Error getting wallet database cursor\n");
604             return DB_CORRUPT;
605         }
606
607         while (true)
608         {
609             // Read next record
610             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
611             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
612             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
613             if (ret == DB_NOTFOUND)
614                 break;
615             else if (ret != 0)
616             {
617                 LogPrintf("Error reading next record from wallet database\n");
618                 return DB_CORRUPT;
619             }
620
621             // Try to be tolerant of single corrupt records:
622             string strType, strErr;
623             if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
624             {
625                 // losing keys is considered a catastrophic error, anything else
626                 // we assume the user can live with:
627                 if (IsKeyType(strType))
628                     result = DB_CORRUPT;
629                 else
630                 {
631                     // Leave other errors alone, if we try to fix them we might make things worse.
632                     fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
633                     if (strType == "tx")
634                         // Rescan if there is a bad transaction record:
635                         SoftSetBoolArg("-rescan", true);
636                 }
637             }
638             if (!strErr.empty())
639                 LogPrintf("%s\n", strErr);
640         }
641         pcursor->close();
642     }
643     catch (boost::thread_interrupted) {
644         throw;
645     }
646     catch (...) {
647         result = DB_CORRUPT;
648     }
649
650     if (fNoncriticalErrors && result == DB_LOAD_OK)
651         result = DB_NONCRITICAL_ERROR;
652
653     // Any wallet corruption at all: skip any rewriting or
654     // upgrading, we don't want to make it worse.
655     if (result != DB_LOAD_OK)
656         return result;
657
658     LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
659
660     LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
661            wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
662
663     // nTimeFirstKey is only reliable if all keys have metadata
664     if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
665         pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
666
667     BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
668         WriteTx(hash, pwallet->mapWallet[hash]);
669
670     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
671     if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
672         return DB_NEED_REWRITE;
673
674     if (wss.nFileVersion < CLIENT_VERSION) // Update
675         WriteVersion(CLIENT_VERSION);
676
677     if (wss.fAnyUnordered)
678         result = ReorderTransactions(pwallet);
679
680     return result;
681 }
682
683 DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash)
684 {
685     pwallet->vchDefaultKey = CPubKey();
686     CWalletScanState wss;
687     bool fNoncriticalErrors = false;
688     DBErrors result = DB_LOAD_OK;
689
690     try {
691         LOCK(pwallet->cs_wallet);
692         int nMinVersion = 0;
693         if (Read((string)"minversion", nMinVersion))
694         {
695             if (nMinVersion > CLIENT_VERSION)
696                 return DB_TOO_NEW;
697             pwallet->LoadMinVersion(nMinVersion);
698         }
699
700         // Get cursor
701         Dbc* pcursor = GetCursor();
702         if (!pcursor)
703         {
704             LogPrintf("Error getting wallet database cursor\n");
705             return DB_CORRUPT;
706         }
707
708         while (true)
709         {
710             // Read next record
711             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
712             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
713             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
714             if (ret == DB_NOTFOUND)
715                 break;
716             else if (ret != 0)
717             {
718                 LogPrintf("Error reading next record from wallet database\n");
719                 return DB_CORRUPT;
720             }
721
722             string strType;
723             ssKey >> strType;
724             if (strType == "tx") {
725                 uint256 hash;
726                 ssKey >> hash;
727
728                 vTxHash.push_back(hash);
729             }
730         }
731         pcursor->close();
732     }
733     catch (boost::thread_interrupted) {
734         throw;
735     }
736     catch (...) {
737         result = DB_CORRUPT;
738     }
739
740     if (fNoncriticalErrors && result == DB_LOAD_OK)
741         result = DB_NONCRITICAL_ERROR;
742
743     return result;
744 }
745
746 DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet)
747 {
748     // build list of wallet TXs
749     vector<uint256> vTxHash;
750     DBErrors err = FindWalletTx(pwallet, vTxHash);
751     if (err != DB_LOAD_OK)
752         return err;
753
754     // erase each wallet TX
755     BOOST_FOREACH (uint256& hash, vTxHash) {
756         if (!EraseTx(hash))
757             return DB_CORRUPT;
758     }
759
760     return DB_LOAD_OK;
761 }
762
763 void ThreadFlushWalletDB(const string& strFile)
764 {
765     // Make this thread recognisable as the wallet flushing thread
766     RenameThread("bitcoin-wallet");
767
768     static bool fOneThread;
769     if (fOneThread)
770         return;
771     fOneThread = true;
772     if (!GetBoolArg("-flushwallet", true))
773         return;
774
775     unsigned int nLastSeen = nWalletDBUpdated;
776     unsigned int nLastFlushed = nWalletDBUpdated;
777     int64_t nLastWalletUpdate = GetTime();
778     while (true)
779     {
780         MilliSleep(500);
781
782         if (nLastSeen != nWalletDBUpdated)
783         {
784             nLastSeen = nWalletDBUpdated;
785             nLastWalletUpdate = GetTime();
786         }
787
788         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
789         {
790             TRY_LOCK(bitdb.cs_db,lockDb);
791             if (lockDb)
792             {
793                 // Don't do this if any databases are in use
794                 int nRefCount = 0;
795                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
796                 while (mi != bitdb.mapFileUseCount.end())
797                 {
798                     nRefCount += (*mi).second;
799                     mi++;
800                 }
801
802                 if (nRefCount == 0)
803                 {
804                     boost::this_thread::interruption_point();
805                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
806                     if (mi != bitdb.mapFileUseCount.end())
807                     {
808                         LogPrint("db", "Flushing wallet.dat\n");
809                         nLastFlushed = nWalletDBUpdated;
810                         int64_t nStart = GetTimeMillis();
811
812                         // Flush wallet.dat so it's self contained
813                         bitdb.CloseDb(strFile);
814                         bitdb.CheckpointLSN(strFile);
815
816                         bitdb.mapFileUseCount.erase(mi++);
817                         LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart);
818                     }
819                 }
820             }
821         }
822     }
823 }
824
825 bool BackupWallet(const CWallet& wallet, const string& strDest)
826 {
827     if (!wallet.fFileBacked)
828         return false;
829     while (true)
830     {
831         {
832             LOCK(bitdb.cs_db);
833             if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
834             {
835                 // Flush log data to the dat file
836                 bitdb.CloseDb(wallet.strWalletFile);
837                 bitdb.CheckpointLSN(wallet.strWalletFile);
838                 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
839
840                 // Copy wallet.dat
841                 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
842                 filesystem::path pathDest(strDest);
843                 if (filesystem::is_directory(pathDest))
844                     pathDest /= wallet.strWalletFile;
845
846                 try {
847 #if BOOST_VERSION >= 104000
848                     filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
849 #else
850                     filesystem::copy_file(pathSrc, pathDest);
851 #endif
852                     LogPrintf("copied wallet.dat to %s\n", pathDest.string());
853                     return true;
854                 } catch(const filesystem::filesystem_error &e) {
855                     LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string(), e.what());
856                     return false;
857                 }
858             }
859         }
860         MilliSleep(100);
861     }
862     return false;
863 }
864
865 //
866 // Try to (very carefully!) recover wallet.dat if there is a problem.
867 //
868 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
869 {
870     // Recovery procedure:
871     // move wallet.dat to wallet.timestamp.bak
872     // Call Salvage with fAggressive=true to
873     // get as much data as possible.
874     // Rewrite salvaged data to wallet.dat
875     // Set -rescan so any missing transactions will be
876     // found.
877     int64_t now = GetTime();
878     std::string newFilename = strprintf("wallet.%d.bak", now);
879
880     int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
881                                       newFilename.c_str(), DB_AUTO_COMMIT);
882     if (result == 0)
883         LogPrintf("Renamed %s to %s\n", filename, newFilename);
884     else
885     {
886         LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
887         return false;
888     }
889
890     std::vector<CDBEnv::KeyValPair> salvagedData;
891     bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
892     if (salvagedData.empty())
893     {
894         LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
895         return false;
896     }
897     LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
898
899     bool fSuccess = allOK;
900     Db* pdbCopy = new Db(&dbenv.dbenv, 0);
901     int ret = pdbCopy->open(NULL,               // Txn pointer
902                             filename.c_str(),   // Filename
903                             "main",             // Logical db name
904                             DB_BTREE,           // Database type
905                             DB_CREATE,          // Flags
906                             0);
907     if (ret > 0)
908     {
909         LogPrintf("Cannot create database file %s\n", filename);
910         return false;
911     }
912     CWallet dummyWallet;
913     CWalletScanState wss;
914
915     DbTxn* ptxn = dbenv.TxnBegin();
916     BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
917     {
918         if (fOnlyKeys)
919         {
920             CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
921             CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
922             string strType, strErr;
923             bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
924                                         wss, strType, strErr);
925             if (!IsKeyType(strType))
926                 continue;
927             if (!fReadOK)
928             {
929                 LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
930                 continue;
931             }
932         }
933         Dbt datKey(&row.first[0], row.first.size());
934         Dbt datValue(&row.second[0], row.second.size());
935         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
936         if (ret2 > 0)
937             fSuccess = false;
938     }
939     ptxn->commit(0);
940     pdbCopy->close(0);
941     delete pdbCopy;
942
943     return fSuccess;
944 }
945
946 bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
947 {
948     return CWalletDB::Recover(dbenv, filename, false);
949 }
950
951 bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
952 {
953     nWalletDBUpdated++;
954     return Write(boost::make_tuple(std::string("destdata"), address, key), value);
955 }
956
957 bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
958 {
959     nWalletDBUpdated++;
960     return Erase(boost::make_tuple(string("destdata"), address, key));
961 }
This page took 0.074873 seconds and 4 git commands to generate.