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