]> Git Repo - VerusCoin.git/blame - src/walletdb.cpp
Shutdown cleanup prep-work
[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
9c7722b7
LD
45bool CWalletDB::WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry)
46{
47 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry);
48}
49
9eace6b1
JG
50bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
51{
9c7722b7 52 return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
9eace6b1
JG
53}
54
55int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
56{
57 list<CAccountingEntry> entries;
58 ListAccountCreditDebit(strAccount, entries);
59
60 int64 nCreditDebit = 0;
61 BOOST_FOREACH (const CAccountingEntry& entry, entries)
62 nCreditDebit += entry.nCreditDebit;
63
64 return nCreditDebit;
65}
66
67void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
68{
69 bool fAllAccounts = (strAccount == "*");
70
71 Dbc* pcursor = GetCursor();
72 if (!pcursor)
73 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
74 unsigned int fFlags = DB_SET_RANGE;
75 loop
76 {
77 // Read next record
6b6aaa16 78 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
9eace6b1
JG
79 if (fFlags == DB_SET_RANGE)
80 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
6b6aaa16 81 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
9eace6b1
JG
82 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
83 fFlags = DB_NEXT;
84 if (ret == DB_NOTFOUND)
85 break;
86 else if (ret != 0)
87 {
88 pcursor->close();
89 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
90 }
91
92 // Unserialize
93 string strType;
94 ssKey >> strType;
95 if (strType != "acentry")
96 break;
97 CAccountingEntry acentry;
98 ssKey >> acentry.strAccount;
99 if (!fAllAccounts && acentry.strAccount != strAccount)
100 break;
101
102 ssValue >> acentry;
9c7722b7 103 ssKey >> acentry.nEntryNo;
9eace6b1
JG
104 entries.push_back(acentry);
105 }
106
107 pcursor->close();
108}
109
110
eed1785f 111DBErrors
9c7722b7
LD
112CWalletDB::ReorderTransactions(CWallet* pwallet)
113{
114 LOCK(pwallet->cs_wallet);
115 // Old wallets didn't have any defined order for transactions
116 // Probably a bad idea to change the output of this
117
118 // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
119 typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
120 typedef multimap<int64, TxPair > TxItems;
121 TxItems txByTime;
122
123 for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it)
124 {
125 CWalletTx* wtx = &((*it).second);
126 txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));
127 }
128 list<CAccountingEntry> acentries;
129 ListAccountCreditDebit("", acentries);
130 BOOST_FOREACH(CAccountingEntry& entry, acentries)
131 {
132 txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
133 }
134
135 int64& nOrderPosNext = pwallet->nOrderPosNext;
136 nOrderPosNext = 0;
137 std::vector<int64> nOrderPosOffsets;
138 for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
139 {
140 CWalletTx *const pwtx = (*it).second.first;
141 CAccountingEntry *const pacentry = (*it).second.second;
142 int64& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
143
144 if (nOrderPos == -1)
145 {
146 nOrderPos = nOrderPosNext++;
147 nOrderPosOffsets.push_back(nOrderPos);
148
149 if (pacentry)
150 // Have to write accounting regardless, since we don't keep it in memory
151 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
152 return DB_LOAD_FAIL;
153 }
154 else
155 {
156 int64 nOrderPosOff = 0;
157 BOOST_FOREACH(const int64& nOffsetStart, nOrderPosOffsets)
158 {
159 if (nOrderPos >= nOffsetStart)
160 ++nOrderPosOff;
161 }
162 nOrderPos += nOrderPosOff;
163 nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
164
165 if (!nOrderPosOff)
166 continue;
167
168 // Since we're changing the order, write it back
169 if (pwtx)
170 {
171 if (!WriteTx(pwtx->GetHash(), *pwtx))
172 return DB_LOAD_FAIL;
173 }
174 else
175 if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry))
176 return DB_LOAD_FAIL;
177 }
178 }
179
180 return DB_LOAD_OK;
181}
182
183
eed1785f
GA
184bool
185ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
186 int& nFileVersion, vector<uint256>& vWalletUpgrade,
187 bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr)
188{
189 try {
190 // Unserialize
191 // Taking advantage of the fact that pair serialization
192 // is just the two items serialized one after the other
193 ssKey >> strType;
194 if (strType == "name")
195 {
196 string strAddress;
197 ssKey >> strAddress;
198 ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()];
199 }
200 else if (strType == "tx")
201 {
202 uint256 hash;
203 ssKey >> hash;
204 CWalletTx& wtx = pwallet->mapWallet[hash];
205 ssValue >> wtx;
ef3988ca
PW
206 CValidationState state;
207 if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid())
eed1785f
GA
208 wtx.BindWallet(pwallet);
209 else
210 {
211 pwallet->mapWallet.erase(hash);
212 return false;
213 }
214
215 // Undo serialize changes in 31600
216 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
217 {
218 if (!ssValue.empty())
219 {
220 char fTmp;
221 char fUnused;
222 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
223 strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s",
224 wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
225 wtx.fTimeReceivedIsTxTime = fTmp;
226 }
227 else
228 {
229 strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
230 wtx.fTimeReceivedIsTxTime = 0;
231 }
232 vWalletUpgrade.push_back(hash);
233 }
234
235 if (wtx.nOrderPos == -1)
236 fAnyUnordered = true;
237
238 //// debug print
239 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
240 //printf(" %12"PRI64d" %s %s %s\n",
241 // wtx.vout[0].nValue,
3f964b3c 242 // DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()).c_str(),
eed1785f
GA
243 // wtx.hashBlock.ToString().substr(0,20).c_str(),
244 // wtx.mapValue["message"].c_str());
245 }
246 else if (strType == "acentry")
247 {
248 string strAccount;
249 ssKey >> strAccount;
250 uint64 nNumber;
251 ssKey >> nNumber;
252 if (nNumber > nAccountingEntryNumber)
253 nAccountingEntryNumber = nNumber;
254
255 if (!fAnyUnordered)
256 {
257 CAccountingEntry acentry;
258 ssValue >> acentry;
259 if (acentry.nOrderPos == -1)
260 fAnyUnordered = true;
261 }
262 }
263 else if (strType == "key" || strType == "wkey")
264 {
265 vector<unsigned char> vchPubKey;
266 ssKey >> vchPubKey;
267 CKey key;
268 if (strType == "key")
269 {
270 CPrivKey pkey;
271 ssValue >> pkey;
272 key.SetPubKey(vchPubKey);
273 if (!key.SetPrivKey(pkey))
274 {
275 strErr = "Error reading wallet database: CPrivKey corrupt";
276 return false;
277 }
278 if (key.GetPubKey() != vchPubKey)
279 {
280 strErr = "Error reading wallet database: CPrivKey pubkey inconsistency";
281 return false;
282 }
283 if (!key.IsValid())
284 {
285 strErr = "Error reading wallet database: invalid CPrivKey";
286 return false;
287 }
288 }
289 else
290 {
291 CWalletKey wkey;
292 ssValue >> wkey;
293 key.SetPubKey(vchPubKey);
294 if (!key.SetPrivKey(wkey.vchPrivKey))
295 {
296 strErr = "Error reading wallet database: CPrivKey corrupt";
297 return false;
298 }
299 if (key.GetPubKey() != vchPubKey)
300 {
301 strErr = "Error reading wallet database: CWalletKey pubkey inconsistency";
302 return false;
303 }
304 if (!key.IsValid())
305 {
306 strErr = "Error reading wallet database: invalid CWalletKey";
307 return false;
308 }
309 }
310 if (!pwallet->LoadKey(key))
311 {
312 strErr = "Error reading wallet database: LoadKey failed";
313 return false;
314 }
315 }
316 else if (strType == "mkey")
317 {
318 unsigned int nID;
319 ssKey >> nID;
320 CMasterKey kMasterKey;
321 ssValue >> kMasterKey;
322 if(pwallet->mapMasterKeys.count(nID) != 0)
323 {
324 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
325 return false;
326 }
327 pwallet->mapMasterKeys[nID] = kMasterKey;
328 if (pwallet->nMasterKeyMaxID < nID)
329 pwallet->nMasterKeyMaxID = nID;
330 }
331 else if (strType == "ckey")
332 {
333 vector<unsigned char> vchPubKey;
334 ssKey >> vchPubKey;
335 vector<unsigned char> vchPrivKey;
336 ssValue >> vchPrivKey;
337 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
338 {
339 strErr = "Error reading wallet database: LoadCryptedKey failed";
340 return false;
341 }
342 fIsEncrypted = true;
343 }
344 else if (strType == "defaultkey")
345 {
346 ssValue >> pwallet->vchDefaultKey;
347 }
348 else if (strType == "pool")
349 {
350 int64 nIndex;
351 ssKey >> nIndex;
352 pwallet->setKeyPool.insert(nIndex);
353 }
354 else if (strType == "version")
355 {
356 ssValue >> nFileVersion;
357 if (nFileVersion == 10300)
358 nFileVersion = 300;
359 }
360 else if (strType == "cscript")
361 {
362 uint160 hash;
363 ssKey >> hash;
364 CScript script;
365 ssValue >> script;
366 if (!pwallet->LoadCScript(script))
367 {
368 strErr = "Error reading wallet database: LoadCScript failed";
369 return false;
370 }
371 }
372 else if (strType == "orderposnext")
373 {
374 ssValue >> pwallet->nOrderPosNext;
375 }
376 } catch (...)
377 {
378 return false;
379 }
380 return true;
381}
382
383static bool IsKeyType(string strType)
384{
385 return (strType== "key" || strType == "wkey" ||
386 strType == "mkey" || strType == "ckey");
387}
388
389DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
9eace6b1 390{
fd61d6f5 391 pwallet->vchDefaultKey = CPubKey();
9eace6b1
JG
392 int nFileVersion = 0;
393 vector<uint256> vWalletUpgrade;
394 bool fIsEncrypted = false;
9c7722b7 395 bool fAnyUnordered = false;
eed1785f
GA
396 bool fNoncriticalErrors = false;
397 DBErrors result = DB_LOAD_OK;
9eace6b1 398
eed1785f 399 try {
9eace6b1
JG
400 LOCK(pwallet->cs_wallet);
401 int nMinVersion = 0;
402 if (Read((string)"minversion", nMinVersion))
403 {
404 if (nMinVersion > CLIENT_VERSION)
405 return DB_TOO_NEW;
406 pwallet->LoadMinVersion(nMinVersion);
407 }
408
409 // Get cursor
410 Dbc* pcursor = GetCursor();
411 if (!pcursor)
412 {
413 printf("Error getting wallet database cursor\n");
414 return DB_CORRUPT;
415 }
416
417 loop
418 {
419 // Read next record
6b6aaa16
PW
420 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
421 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
9eace6b1
JG
422 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
423 if (ret == DB_NOTFOUND)
424 break;
425 else if (ret != 0)
426 {
427 printf("Error reading next record from wallet database\n");
428 return DB_CORRUPT;
429 }
430
eed1785f
GA
431 // Try to be tolerant of single corrupt records:
432 string strType, strErr;
433 if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion,
434 vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr))
9eace6b1 435 {
eed1785f
GA
436 // losing keys is considered a catastrophic error, anything else
437 // we assume the user can live with:
438 if (IsKeyType(strType))
439 result = DB_CORRUPT;
9eace6b1
JG
440 else
441 {
eed1785f
GA
442 // Leave other errors alone, if we try to fix them we might make things worse.
443 fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
444 if (strType == "tx")
445 // Rescan if there is a bad transaction record:
446 SoftSetBoolArg("-rescan", true);
9eace6b1 447 }
da7b8c12 448 }
eed1785f
GA
449 if (!strErr.empty())
450 printf("%s\n", strErr.c_str());
9eace6b1
JG
451 }
452 pcursor->close();
453 }
eed1785f
GA
454 catch (...)
455 {
456 result = DB_CORRUPT;
457 }
9eace6b1 458
eed1785f
GA
459 if (fNoncriticalErrors && result == DB_LOAD_OK)
460 result = DB_NONCRITICAL_ERROR;
461
462 // Any wallet corruption at all: skip any rewriting or
463 // upgrading, we don't want to make it worse.
464 if (result != DB_LOAD_OK)
465 return result;
9eace6b1
JG
466
467 printf("nFileVersion = %d\n", nFileVersion);
468
eed1785f
GA
469 BOOST_FOREACH(uint256 hash, vWalletUpgrade)
470 WriteTx(hash, pwallet->mapWallet[hash]);
9eace6b1
JG
471
472 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
473 if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
474 return DB_NEED_REWRITE;
475
476 if (nFileVersion < CLIENT_VERSION) // Update
477 WriteVersion(CLIENT_VERSION);
478
9c7722b7 479 if (fAnyUnordered)
eed1785f 480 result = ReorderTransactions(pwallet);
9c7722b7 481
eed1785f 482 return result;
9eace6b1
JG
483}
484
485void ThreadFlushWalletDB(void* parg)
486{
96931d6f
GS
487 // Make this thread recognisable as the wallet flushing thread
488 RenameThread("bitcoin-wallet");
489
9eace6b1
JG
490 const string& strFile = ((const string*)parg)[0];
491 static bool fOneThread;
492 if (fOneThread)
493 return;
494 fOneThread = true;
495 if (!GetBoolArg("-flushwallet", true))
496 return;
497
498 unsigned int nLastSeen = nWalletDBUpdated;
499 unsigned int nLastFlushed = nWalletDBUpdated;
500 int64 nLastWalletUpdate = GetTime();
501 while (!fShutdown)
502 {
503 Sleep(500);
504
505 if (nLastSeen != nWalletDBUpdated)
506 {
507 nLastSeen = nWalletDBUpdated;
508 nLastWalletUpdate = GetTime();
509 }
510
511 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
512 {
cd9696fc 513 TRY_LOCK(bitdb.cs_db,lockDb);
9eace6b1
JG
514 if (lockDb)
515 {
516 // Don't do this if any databases are in use
517 int nRefCount = 0;
ffe8b77a
JG
518 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
519 while (mi != bitdb.mapFileUseCount.end())
9eace6b1
JG
520 {
521 nRefCount += (*mi).second;
522 mi++;
523 }
524
525 if (nRefCount == 0 && !fShutdown)
526 {
ffe8b77a
JG
527 map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
528 if (mi != bitdb.mapFileUseCount.end())
9eace6b1 529 {
9eace6b1
JG
530 printf("Flushing wallet.dat\n");
531 nLastFlushed = nWalletDBUpdated;
532 int64 nStart = GetTimeMillis();
533
534 // Flush wallet.dat so it's self contained
ffe8b77a 535 bitdb.CloseDb(strFile);
cd9696fc 536 bitdb.CheckpointLSN(strFile);
9eace6b1 537
ffe8b77a 538 bitdb.mapFileUseCount.erase(mi++);
9eace6b1
JG
539 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
540 }
541 }
542 }
543 }
544 }
545}
546
547bool BackupWallet(const CWallet& wallet, const string& strDest)
548{
549 if (!wallet.fFileBacked)
550 return false;
551 while (!fShutdown)
552 {
553 {
cd9696fc 554 LOCK(bitdb.cs_db);
ffe8b77a 555 if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
9eace6b1
JG
556 {
557 // Flush log data to the dat file
ffe8b77a 558 bitdb.CloseDb(wallet.strWalletFile);
cd9696fc 559 bitdb.CheckpointLSN(wallet.strWalletFile);
ffe8b77a 560 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
9eace6b1
JG
561
562 // Copy wallet.dat
563 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
564 filesystem::path pathDest(strDest);
565 if (filesystem::is_directory(pathDest))
566 pathDest /= wallet.strWalletFile;
567
568 try {
569#if BOOST_VERSION >= 104000
570 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
571#else
572 filesystem::copy_file(pathSrc, pathDest);
573#endif
574 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
575 return true;
576 } catch(const filesystem::filesystem_error &e) {
577 printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
578 return false;
579 }
580 }
581 }
582 Sleep(100);
583 }
584 return false;
585}
eed1785f
GA
586
587//
588// Try to (very carefully!) recover wallet.dat if there is a problem.
589//
590bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
591{
592 // Recovery procedure:
593 // move wallet.dat to wallet.timestamp.bak
594 // Call Salvage with fAggressive=true to
595 // get as much data as possible.
596 // Rewrite salvaged data to wallet.dat
597 // Set -rescan so any missing transactions will be
598 // found.
599 int64 now = GetTime();
600 std::string newFilename = strprintf("wallet.%"PRI64d".bak", now);
601
602 int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
603 newFilename.c_str(), DB_AUTO_COMMIT);
604 if (result == 0)
605 printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
606 else
607 {
608 printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
609 return false;
610 }
611
612 std::vector<CDBEnv::KeyValPair> salvagedData;
613 bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
614 if (salvagedData.empty())
615 {
616 printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
617 return false;
618 }
619 printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size());
620
621 bool fSuccess = allOK;
622 Db* pdbCopy = new Db(&dbenv.dbenv, 0);
623 int ret = pdbCopy->open(NULL, // Txn pointer
624 filename.c_str(), // Filename
625 "main", // Logical db name
626 DB_BTREE, // Database type
627 DB_CREATE, // Flags
628 0);
629 if (ret > 0)
630 {
631 printf("Cannot create database file %s\n", filename.c_str());
632 return false;
633 }
634 CWallet dummyWallet;
635 int nFileVersion = 0;
636 vector<uint256> vWalletUpgrade;
637 bool fIsEncrypted = false;
638 bool fAnyUnordered = false;
639
640 DbTxn* ptxn = dbenv.TxnBegin();
641 BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData)
642 {
643 if (fOnlyKeys)
644 {
645 CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
646 CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
647 string strType, strErr;
648 bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
649 nFileVersion, vWalletUpgrade,
650 fIsEncrypted, fAnyUnordered,
651 strType, strErr);
652 if (!IsKeyType(strType))
653 continue;
654 if (!fReadOK)
655 {
656 printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
657 continue;
658 }
659 }
660 Dbt datKey(&row.first[0], row.first.size());
661 Dbt datValue(&row.second[0], row.second.size());
662 int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
663 if (ret2 > 0)
664 fSuccess = false;
665 }
666 ptxn->commit(0);
667 pdbCopy->close(0);
668 delete pdbCopy;
669
670 return fSuccess;
671}
672
673bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
674{
675 return CWalletDB::Recover(dbenv, filename, false);
676}
This page took 0.126417 seconds and 4 git commands to generate.