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