]> Git Repo - VerusCoin.git/blame - src/walletdb.cpp
More CScript unit tests.
[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
9eace6b1
JG
16//
17// CWalletDB
18//
19
20bool CWalletDB::WriteName(const string& strAddress, const string& strName)
21{
22 nWalletDBUpdated++;
23 return Write(make_pair(string("name"), strAddress), strName);
24}
25
26bool 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
34bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
35{
36 account.SetNull();
37 return Read(make_pair(string("acc"), strAccount), account);
38}
39
40bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
41{
42 return Write(make_pair(string("acc"), strAccount), account);
43}
44
45bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
46{
47 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
48}
49
50int64 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
62void 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
6b6aaa16 73 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
9eace6b1
JG
74 if (fFlags == DB_SET_RANGE)
75 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
6b6aaa16 76 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
9eace6b1
JG
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
105int 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
6b6aaa16
PW
134 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
135 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
9eace6b1
JG
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());
82e6b92b 188 //printf(" %12"PRI64d" %s %s %s\n",
9eace6b1
JG
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
324void 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 {
cd9696fc 349 TRY_LOCK(bitdb.cs_db,lockDb);
9eace6b1
JG
350 if (lockDb)
351 {
352 // Don't do this if any databases are in use
353 int nRefCount = 0;
ffe8b77a
JG
354 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
355 while (mi != bitdb.mapFileUseCount.end())
9eace6b1
JG
356 {
357 nRefCount += (*mi).second;
358 mi++;
359 }
360
361 if (nRefCount == 0 && !fShutdown)
362 {
ffe8b77a
JG
363 map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
364 if (mi != bitdb.mapFileUseCount.end())
9eace6b1 365 {
9eace6b1
JG
366 printf("Flushing wallet.dat\n");
367 nLastFlushed = nWalletDBUpdated;
368 int64 nStart = GetTimeMillis();
369
370 // Flush wallet.dat so it's self contained
ffe8b77a 371 bitdb.CloseDb(strFile);
cd9696fc 372 bitdb.CheckpointLSN(strFile);
9eace6b1 373
ffe8b77a 374 bitdb.mapFileUseCount.erase(mi++);
9eace6b1
JG
375 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
376 }
377 }
378 }
379 }
380 }
381}
382
383bool BackupWallet(const CWallet& wallet, const string& strDest)
384{
385 if (!wallet.fFileBacked)
386 return false;
387 while (!fShutdown)
388 {
389 {
cd9696fc 390 LOCK(bitdb.cs_db);
ffe8b77a 391 if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
9eace6b1
JG
392 {
393 // Flush log data to the dat file
ffe8b77a 394 bitdb.CloseDb(wallet.strWalletFile);
cd9696fc 395 bitdb.CheckpointLSN(wallet.strWalletFile);
ffe8b77a 396 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
9eace6b1
JG
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.071487 seconds and 4 git commands to generate.