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