walletdb.cpp 23 KB

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