]> Git Repo - VerusCoin.git/blob - src/walletdb.cpp
Use the QueueShutdown signal to stop accepting new RPC connections
[VerusCoin.git] / src / walletdb.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "walletdb.h"
7 #include "wallet.h"
8 #include <boost/filesystem.hpp>
9
10 using namespace std;
11 using namespace boost;
12
13
14 static uint64 nAccountingEntryNumber = 0;
15
16 //
17 // CWalletDB
18 //
19
20 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
21 {
22     nWalletDBUpdated++;
23     return Write(make_pair(string("name"), strAddress), strName);
24 }
25
26 bool CWalletDB::EraseName(const string& strAddress)
27 {
28     // This should only be used for sending addresses, never for receiving addresses,
29     // receiving addresses must always have an address book entry if they're not change return.
30     nWalletDBUpdated++;
31     return Erase(make_pair(string("name"), strAddress));
32 }
33
34 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
35 {
36     account.SetNull();
37     return Read(make_pair(string("acc"), strAccount), account);
38 }
39
40 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
41 {
42     return Write(make_pair(string("acc"), strAccount), account);
43 }
44
45 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
46 {
47     return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
48 }
49
50 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
51 {
52     list<CAccountingEntry> entries;
53     ListAccountCreditDebit(strAccount, entries);
54
55     int64 nCreditDebit = 0;
56     BOOST_FOREACH (const CAccountingEntry& entry, entries)
57         nCreditDebit += entry.nCreditDebit;
58
59     return nCreditDebit;
60 }
61
62 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
63 {
64     bool fAllAccounts = (strAccount == "*");
65
66     Dbc* pcursor = GetCursor();
67     if (!pcursor)
68         throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
69     unsigned int fFlags = DB_SET_RANGE;
70     loop
71     {
72         // Read next record
73         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
74         if (fFlags == DB_SET_RANGE)
75             ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
76         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
77         int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
78         fFlags = DB_NEXT;
79         if (ret == DB_NOTFOUND)
80             break;
81         else if (ret != 0)
82         {
83             pcursor->close();
84             throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
85         }
86
87         // Unserialize
88         string strType;
89         ssKey >> strType;
90         if (strType != "acentry")
91             break;
92         CAccountingEntry acentry;
93         ssKey >> acentry.strAccount;
94         if (!fAllAccounts && acentry.strAccount != strAccount)
95             break;
96
97         ssValue >> acentry;
98         entries.push_back(acentry);
99     }
100
101     pcursor->close();
102 }
103
104
105 int CWalletDB::LoadWallet(CWallet* pwallet)
106 {
107     pwallet->vchDefaultKey.clear();
108     int nFileVersion = 0;
109     vector<uint256> vWalletUpgrade;
110     bool fIsEncrypted = false;
111
112     //// todo: shouldn't we catch exceptions and try to recover and continue?
113     {
114         LOCK(pwallet->cs_wallet);
115         int nMinVersion = 0;
116         if (Read((string)"minversion", nMinVersion))
117         {
118             if (nMinVersion > CLIENT_VERSION)
119                 return DB_TOO_NEW;
120             pwallet->LoadMinVersion(nMinVersion);
121         }
122
123         // Get cursor
124         Dbc* pcursor = GetCursor();
125         if (!pcursor)
126         {
127             printf("Error getting wallet database cursor\n");
128             return DB_CORRUPT;
129         }
130
131         loop
132         {
133             // Read next record
134             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
135             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
136             int ret = ReadAtCursor(pcursor, ssKey, ssValue);
137             if (ret == DB_NOTFOUND)
138                 break;
139             else if (ret != 0)
140             {
141                 printf("Error reading next record from wallet database\n");
142                 return DB_CORRUPT;
143             }
144
145             // Unserialize
146             // Taking advantage of the fact that pair serialization
147             // is just the two items serialized one after the other
148             string strType;
149             ssKey >> strType;
150             if (strType == "name")
151             {
152                 string strAddress;
153                 ssKey >> strAddress;
154                 ssValue >> pwallet->mapAddressBook[strAddress];
155             }
156             else if (strType == "tx")
157             {
158                 uint256 hash;
159                 ssKey >> hash;
160                 CWalletTx& wtx = pwallet->mapWallet[hash];
161                 ssValue >> wtx;
162                 wtx.BindWallet(pwallet);
163
164                 if (wtx.GetHash() != hash)
165                     printf("Error in wallet.dat, hash mismatch\n");
166
167                 // Undo serialize changes in 31600
168                 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
169                 {
170                     if (!ssValue.empty())
171                     {
172                         char fTmp;
173                         char fUnused;
174                         ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
175                         printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
176                         wtx.fTimeReceivedIsTxTime = fTmp;
177                     }
178                     else
179                     {
180                         printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
181                         wtx.fTimeReceivedIsTxTime = 0;
182                     }
183                     vWalletUpgrade.push_back(hash);
184                 }
185
186                 //// debug print
187                 //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
188                 //printf(" %12"PRI64d"  %s  %s  %s\n",
189                 //    wtx.vout[0].nValue,
190                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
191                 //    wtx.hashBlock.ToString().substr(0,20).c_str(),
192                 //    wtx.mapValue["message"].c_str());
193             }
194             else if (strType == "acentry")
195             {
196                 string strAccount;
197                 ssKey >> strAccount;
198                 uint64 nNumber;
199                 ssKey >> nNumber;
200                 if (nNumber > nAccountingEntryNumber)
201                     nAccountingEntryNumber = nNumber;
202             }
203             else if (strType == "key" || strType == "wkey")
204             {
205                 vector<unsigned char> vchPubKey;
206                 ssKey >> vchPubKey;
207                 CKey key;
208                 if (strType == "key")
209                 {
210                     CPrivKey pkey;
211                     ssValue >> pkey;
212                     key.SetPubKey(vchPubKey);
213                     key.SetPrivKey(pkey);
214                     if (key.GetPubKey() != vchPubKey)
215                     {
216                         printf("Error reading wallet database: CPrivKey pubkey inconsistency\n");
217                         return DB_CORRUPT;
218                     }
219                     if (!key.IsValid())
220                     {
221                         printf("Error reading wallet database: invalid CPrivKey\n");
222                         return DB_CORRUPT;
223                     }
224                 }
225                 else
226                 {
227                     CWalletKey wkey;
228                     ssValue >> wkey;
229                     key.SetPubKey(vchPubKey);
230                     key.SetPrivKey(wkey.vchPrivKey);
231                     if (key.GetPubKey() != vchPubKey)
232                     {
233                         printf("Error reading wallet database: CWalletKey pubkey inconsistency\n");
234                         return DB_CORRUPT;
235                     }
236                     if (!key.IsValid())
237                     {
238                         printf("Error reading wallet database: invalid CWalletKey\n");
239                         return DB_CORRUPT;
240                     }
241                 }
242                 if (!pwallet->LoadKey(key))
243                 {
244                     printf("Error reading wallet database: LoadKey failed\n");
245                     return DB_CORRUPT;
246                 }
247             }
248             else if (strType == "mkey")
249             {
250                 unsigned int nID;
251                 ssKey >> nID;
252                 CMasterKey kMasterKey;
253                 ssValue >> kMasterKey;
254                 if(pwallet->mapMasterKeys.count(nID) != 0)
255                 {
256                     printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID);
257                     return DB_CORRUPT;
258                 }
259                 pwallet->mapMasterKeys[nID] = kMasterKey;
260                 if (pwallet->nMasterKeyMaxID < nID)
261                     pwallet->nMasterKeyMaxID = nID;
262             }
263             else if (strType == "ckey")
264             {
265                 vector<unsigned char> vchPubKey;
266                 ssKey >> vchPubKey;
267                 vector<unsigned char> vchPrivKey;
268                 ssValue >> vchPrivKey;
269                 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
270                 {
271                     printf("Error reading wallet database: LoadCryptedKey failed\n");
272                     return DB_CORRUPT;
273                 }
274                 fIsEncrypted = true;
275             }
276             else if (strType == "defaultkey")
277             {
278                 ssValue >> pwallet->vchDefaultKey;
279             }
280             else if (strType == "pool")
281             {
282                 int64 nIndex;
283                 ssKey >> nIndex;
284                 pwallet->setKeyPool.insert(nIndex);
285             }
286             else if (strType == "version")
287             {
288                 ssValue >> nFileVersion;
289                 if (nFileVersion == 10300)
290                     nFileVersion = 300;
291             }
292             else if (strType == "cscript")
293             {
294                 uint160 hash;
295                 ssKey >> hash;
296                 CScript script;
297                 ssValue >> script;
298                 if (!pwallet->LoadCScript(script))
299                 {
300                     printf("Error reading wallet database: LoadCScript failed\n");
301                     return DB_CORRUPT;
302                 }
303             }
304         }
305         pcursor->close();
306     }
307
308     BOOST_FOREACH(uint256 hash, vWalletUpgrade)
309         WriteTx(hash, pwallet->mapWallet[hash]);
310
311     printf("nFileVersion = %d\n", nFileVersion);
312
313
314     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
315     if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
316         return DB_NEED_REWRITE;
317
318     if (nFileVersion < CLIENT_VERSION) // Update
319         WriteVersion(CLIENT_VERSION);
320
321     return DB_LOAD_OK;
322 }
323
324 void ThreadFlushWalletDB(void* parg)
325 {
326     const string& strFile = ((const string*)parg)[0];
327     static bool fOneThread;
328     if (fOneThread)
329         return;
330     fOneThread = true;
331     if (!GetBoolArg("-flushwallet", true))
332         return;
333
334     unsigned int nLastSeen = nWalletDBUpdated;
335     unsigned int nLastFlushed = nWalletDBUpdated;
336     int64 nLastWalletUpdate = GetTime();
337     while (!fShutdown)
338     {
339         Sleep(500);
340
341         if (nLastSeen != nWalletDBUpdated)
342         {
343             nLastSeen = nWalletDBUpdated;
344             nLastWalletUpdate = GetTime();
345         }
346
347         if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
348         {
349             TRY_LOCK(bitdb.cs_db,lockDb);
350             if (lockDb)
351             {
352                 // Don't do this if any databases are in use
353                 int nRefCount = 0;
354                 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
355                 while (mi != bitdb.mapFileUseCount.end())
356                 {
357                     nRefCount += (*mi).second;
358                     mi++;
359                 }
360
361                 if (nRefCount == 0 && !fShutdown)
362                 {
363                     map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
364                     if (mi != bitdb.mapFileUseCount.end())
365                     {
366                         printf("Flushing wallet.dat\n");
367                         nLastFlushed = nWalletDBUpdated;
368                         int64 nStart = GetTimeMillis();
369
370                         // Flush wallet.dat so it's self contained
371                         bitdb.CloseDb(strFile);
372                         bitdb.CheckpointLSN(strFile);
373
374                         bitdb.mapFileUseCount.erase(mi++);
375                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
376                     }
377                 }
378             }
379         }
380     }
381 }
382
383 bool BackupWallet(const CWallet& wallet, const string& strDest)
384 {
385     if (!wallet.fFileBacked)
386         return false;
387     while (!fShutdown)
388     {
389         {
390             LOCK(bitdb.cs_db);
391             if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
392             {
393                 // Flush log data to the dat file
394                 bitdb.CloseDb(wallet.strWalletFile);
395                 bitdb.CheckpointLSN(wallet.strWalletFile);
396                 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
397
398                 // Copy wallet.dat
399                 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
400                 filesystem::path pathDest(strDest);
401                 if (filesystem::is_directory(pathDest))
402                     pathDest /= wallet.strWalletFile;
403
404                 try {
405 #if BOOST_VERSION >= 104000
406                     filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
407 #else
408                     filesystem::copy_file(pathSrc, pathDest);
409 #endif
410                     printf("copied wallet.dat to %s\n", pathDest.string().c_str());
411                     return true;
412                 } catch(const filesystem::filesystem_error &e) {
413                     printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
414                     return false;
415                 }
416             }
417         }
418         Sleep(100);
419     }
420     return false;
421 }
This page took 0.047627 seconds and 4 git commands to generate.