X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fwalletdb.cpp;h=ad24172032814d3825d33c97ed51c361eed99a54;hp=ac355abcb1415c5dbece91ccdbc4c09078752508;hb=84a4a7763f386934da90e2bd1e355b70023fa9ca;hpb=9cc27ae536be74b221605119292e7ee67b520fe1 diff --git a/src/walletdb.cpp b/src/walletdb.cpp index ac355ab..ad24172 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -1,6 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2012 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,10 +12,7 @@ using namespace boost; static uint64 nAccountingEntryNumber = 0; - -extern CCriticalSection cs_db; -extern map mapFileUseCount; -extern void CloseDb(const string& strFile); +extern bool fWalletUnlockMintOnly; // // CWalletDB @@ -47,9 +43,14 @@ bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) return Write(make_pair(string("acc"), strAccount), account); } +bool CWalletDB::WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry) +{ + return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry); +} + bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) { - return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); + return WriteAccountingEntry(++nAccountingEntryNumber, acentry); } int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) @@ -100,6 +101,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list> acentry; + ssKey >> acentry.nEntryNo; entries.push_back(acentry); } @@ -107,214 +109,365 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, listvchDefaultKey.clear(); - int nFileVersion = 0; - vector vWalletUpgrade; - bool fIsEncrypted = false; + LOCK(pwallet->cs_wallet); + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. + typedef pair TxPair; + typedef multimap TxItems; + TxItems txByTime; - //// todo: shouldn't we catch exceptions and try to recover and continue? + for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { - LOCK(pwallet->cs_wallet); - int nMinVersion = 0; - if (Read((string)"minversion", nMinVersion)) - { - if (nMinVersion > CLIENT_VERSION) - return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + ListAccountCreditDebit("", acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) + int64& nOrderPosNext = pwallet->nOrderPosNext; + nOrderPosNext = 0; + std::vector nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + CAccountingEntry *const pacentry = (*it).second.second; + int64& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + + if (nOrderPos == -1) { - printf("Error getting wallet database cursor\n"); - return DB_CORRUPT; - } + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); - loop + if (pacentry) + // Have to write accounting regardless, since we don't keep it in memory + if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + else { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) + int64 nOrderPosOff = 0; + BOOST_FOREACH(const int64& nOffsetStart, nOrderPosOffsets) { - printf("Error reading next record from wallet database\n"); - return DB_CORRUPT; + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - string strType; - ssKey >> strType; - if (strType == "name") + // Since we're changing the order, write it back + if (pwtx) { - string strAddress; - ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[strAddress]; + if (!WriteTx(pwtx->GetHash(), *pwtx)) + return DB_LOAD_FAIL; } - else if (strType == "tx") - { - uint256 hash; - ssKey >> hash; - CWalletTx& wtx = pwallet->mapWallet[hash]; - ssValue >> wtx; - wtx.BindWallet(pwallet); + else + if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + + return DB_LOAD_OK; +} + - if (wtx.GetHash() != hash) - printf("Error in wallet.dat, hash mismatch\n"); +bool +ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, + int& nFileVersion, vector& vWalletUpgrade, + bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr) +{ + try { + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = pwallet->mapWallet[hash]; + ssValue >> wtx; + if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) + wtx.BindWallet(pwallet); + else + { + pwallet->mapWallet.erase(hash); + return false; + } - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!ssValue.empty()) { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); - wtx.fTimeReceivedIsTxTime = 0; - } - vWalletUpgrade.push_back(hash); + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", + wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = fTmp; } - - //// debug print - //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12"PRI64d" %s %s %s\n", - // wtx.vout[0].nValue, - // DateTimeStrFormat(wtx.GetBlockTime()).c_str(), - // wtx.hashBlock.ToString().substr(0,20).c_str(), - // wtx.mapValue["message"].c_str()); + else + { + strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); + wtx.fTimeReceivedIsTxTime = 0; + } + vWalletUpgrade.push_back(hash); } - else if (strType == "acentry") + + if (wtx.nOrderPos == -1) + fAnyUnordered = true; + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12"PRI64d" %s %s %s\n", + // wtx.vout[0].nValue, + // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(), + // wtx.hashBlock.ToString().substr(0,20).c_str(), + // wtx.mapValue["message"].c_str()); + } + else if (strType == "acentry") + { + string strAccount; + ssKey >> strAccount; + uint64 nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + + if (!fAnyUnordered) { - string strAccount; - ssKey >> strAccount; - uint64 nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; + CAccountingEntry acentry; + ssValue >> acentry; + if (acentry.nOrderPos == -1) + fAnyUnordered = true; } - else if (strType == "key" || strType == "wkey") + } + else if (strType == "key" || strType == "wkey") + { + vector vchPubKey; + ssKey >> vchPubKey; + CKey key; + if (strType == "key") { - vector vchPubKey; - ssKey >> vchPubKey; - CKey key; - if (strType == "key") + CPrivKey pkey; + ssValue >> pkey; + key.SetPubKey(vchPubKey); + if (!key.SetPrivKey(pkey)) { - CPrivKey pkey; - ssValue >> pkey; - key.SetPubKey(vchPubKey); - key.SetPrivKey(pkey); - if (key.GetPubKey() != vchPubKey) - { - printf("Error reading wallet database: CPrivKey pubkey inconsistency\n"); - return DB_CORRUPT; - } - if (!key.IsValid()) - { - printf("Error reading wallet database: invalid CPrivKey\n"); - return DB_CORRUPT; - } + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; } - else + if (key.GetPubKey() != vchPubKey) { - CWalletKey wkey; - ssValue >> wkey; - key.SetPubKey(vchPubKey); - key.SetPrivKey(wkey.vchPrivKey); - if (key.GetPubKey() != vchPubKey) - { - printf("Error reading wallet database: CWalletKey pubkey inconsistency\n"); - return DB_CORRUPT; - } - if (!key.IsValid()) - { - printf("Error reading wallet database: invalid CWalletKey\n"); - return DB_CORRUPT; - } + strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + return false; } - if (!pwallet->LoadKey(key)) + if (!key.IsValid()) { - printf("Error reading wallet database: LoadKey failed\n"); - return DB_CORRUPT; + strErr = "Error reading wallet database: invalid CPrivKey"; + return false; } } - else if (strType == "mkey") + else { - unsigned int nID; - ssKey >> nID; - CMasterKey kMasterKey; - ssValue >> kMasterKey; - if(pwallet->mapMasterKeys.count(nID) != 0) + CWalletKey wkey; + ssValue >> wkey; + key.SetPubKey(vchPubKey); + if (!key.SetPrivKey(wkey.vchPrivKey)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (key.GetPubKey() != vchPubKey) { - printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID); - return DB_CORRUPT; + strErr = "Error reading wallet database: CWalletKey pubkey inconsistency"; + return false; } - pwallet->mapMasterKeys[nID] = kMasterKey; - if (pwallet->nMasterKeyMaxID < nID) - pwallet->nMasterKeyMaxID = nID; - } - else if (strType == "ckey") - { - vector vchPubKey; - ssKey >> vchPubKey; - vector vchPrivKey; - ssValue >> vchPrivKey; - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) + if (!key.IsValid()) { - printf("Error reading wallet database: LoadCryptedKey failed\n"); - return DB_CORRUPT; + strErr = "Error reading wallet database: invalid CWalletKey"; + return false; } - fIsEncrypted = true; } - else if (strType == "defaultkey") + if (!pwallet->LoadKey(key)) { - ssValue >> pwallet->vchDefaultKey; + strErr = "Error reading wallet database: LoadKey failed"; + return false; } - else if (strType == "pool") + } + else if (strType == "mkey") + { + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if(pwallet->mapMasterKeys.count(nID) != 0) { - int64 nIndex; - ssKey >> nIndex; - pwallet->setKeyPool.insert(nIndex); + strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); + return false; } - else if (strType == "version") + pwallet->mapMasterKeys[nID] = kMasterKey; + if (pwallet->nMasterKeyMaxID < nID) + pwallet->nMasterKeyMaxID = nID; + } + else if (strType == "ckey") + { + vector vchPubKey; + ssKey >> vchPubKey; + vector vchPrivKey; + ssValue >> vchPrivKey; + if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { - ssValue >> nFileVersion; - if (nFileVersion == 10300) - nFileVersion = 300; + strErr = "Error reading wallet database: LoadCryptedKey failed"; + return false; } - else if (strType == "cscript") + fIsEncrypted = true; + } + else if (strType == "defaultkey") + { + ssValue >> pwallet->vchDefaultKey; + } + else if (strType == "pool") + { + int64 nIndex; + ssKey >> nIndex; + pwallet->setKeyPool.insert(nIndex); + } + else if (strType == "version") + { + ssValue >> nFileVersion; + if (nFileVersion == 10300) + nFileVersion = 300; + } + else if (strType == "cscript") + { + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(script)) { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->LoadCScript(script)) + strErr = "Error reading wallet database: LoadCScript failed"; + return false; + } + } + else if (strType == "orderposnext") + { + ssValue >> pwallet->nOrderPosNext; + } + } catch (...) + { + return false; + } + return true; +} + +static bool IsKeyType(string strType) +{ + return (strType== "key" || strType == "wkey" || + strType == "mkey" || strType == "ckey"); +} + +DBErrors CWalletDB::LoadWallet(CWallet* pwallet) +{ + pwallet->vchDefaultKey = CPubKey(); + int nFileVersion = 0; + vector vWalletUpgrade; + bool fIsEncrypted = false; + bool fAnyUnordered = false; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + printf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + loop + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + printf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + // Try to be tolerant of single corrupt records: + string strType, strErr; + if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion, + vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr)) + { + // losing keys is considered a catastrophic error, anything else + // we assume the user can live with: + if (IsKeyType(strType)) + result = DB_CORRUPT; + else { - printf("Error reading wallet database: LoadCScript failed\n"); - return DB_CORRUPT; + // Leave other errors alone, if we try to fix them we might make things worse. + fNoncriticalErrors = true; // ... but do warn the user there is something wrong. + if (strType == "tx") + // Rescan if there is a bad transaction record: + SoftSetBoolArg("-rescan", true); } } + if (!strErr.empty()) + printf("%s\n", strErr.c_str()); } pcursor->close(); } + catch (...) + { + result = DB_CORRUPT; + } - BOOST_FOREACH(uint256 hash, vWalletUpgrade) - WriteTx(hash, pwallet->mapWallet[hash]); + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + // Any wallet corruption at all: skip any rewriting or + // upgrading, we don't want to make it worse. + if (result != DB_LOAD_OK) + return result; printf("nFileVersion = %d\n", nFileVersion); + BOOST_FOREACH(uint256 hash, vWalletUpgrade) + WriteTx(hash, pwallet->mapWallet[hash]); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) @@ -323,11 +476,17 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (nFileVersion < CLIENT_VERSION) // Update WriteVersion(CLIENT_VERSION); - return DB_LOAD_OK; + if (fAnyUnordered) + result = ReorderTransactions(pwallet); + + return result; } void ThreadFlushWalletDB(void* parg) { + // Make this thread recognisable as the wallet flushing thread + RenameThread("bitcoin-wallet"); + const string& strFile = ((const string*)parg)[0]; static bool fOneThread; if (fOneThread) @@ -351,13 +510,13 @@ void ThreadFlushWalletDB(void* parg) if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) { - TRY_LOCK(cs_db,lockDb); + TRY_LOCK(bitdb.cs_db,lockDb); if (lockDb) { // Don't do this if any databases are in use int nRefCount = 0; - map::iterator mi = mapFileUseCount.begin(); - while (mi != mapFileUseCount.end()) + map::iterator mi = bitdb.mapFileUseCount.begin(); + while (mi != bitdb.mapFileUseCount.end()) { nRefCount += (*mi).second; mi++; @@ -365,20 +524,18 @@ void ThreadFlushWalletDB(void* parg) if (nRefCount == 0 && !fShutdown) { - map::iterator mi = mapFileUseCount.find(strFile); - if (mi != mapFileUseCount.end()) + map::iterator mi = bitdb.mapFileUseCount.find(strFile); + if (mi != bitdb.mapFileUseCount.end()) { - printf("%s ", DateTimeStrFormat(GetTime()).c_str()); printf("Flushing wallet.dat\n"); nLastFlushed = nWalletDBUpdated; int64 nStart = GetTimeMillis(); // Flush wallet.dat so it's self contained - CloseDb(strFile); - dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(strFile.c_str(), 0); + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); - mapFileUseCount.erase(mi++); + bitdb.mapFileUseCount.erase(mi++); printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); } } @@ -394,14 +551,13 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) while (!fShutdown) { { - LOCK(cs_db); - if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file - CloseDb(wallet.strWalletFile); - dbenv.txn_checkpoint(0, 0, 0); - dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0); - mapFileUseCount.erase(wallet.strWalletFile); + bitdb.CloseDb(wallet.strWalletFile); + bitdb.CheckpointLSN(wallet.strWalletFile); + bitdb.mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet.dat filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; @@ -427,3 +583,94 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) } return false; } + +// +// Try to (very carefully!) recover wallet.dat if there is a problem. +// +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) +{ + // Recovery procedure: + // move wallet.dat to wallet.timestamp.bak + // Call Salvage with fAggressive=true to + // get as much data as possible. + // Rewrite salvaged data to wallet.dat + // Set -rescan so any missing transactions will be + // found. + int64 now = GetTime(); + std::string newFilename = strprintf("wallet.%"PRI64d".bak", now); + + int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, + newFilename.c_str(), DB_AUTO_COMMIT); + if (result == 0) + printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); + else + { + printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); + return false; + } + + std::vector salvagedData; + bool allOK = dbenv.Salvage(newFilename, true, salvagedData); + if (salvagedData.empty()) + { + printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); + return false; + } + printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size()); + + bool fSuccess = allOK; + Db* pdbCopy = new Db(&dbenv.dbenv, 0); + int ret = pdbCopy->open(NULL, // Txn pointer + filename.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", filename.c_str()); + return false; + } + CWallet dummyWallet; + int nFileVersion = 0; + vector vWalletUpgrade; + bool fIsEncrypted = false; + bool fAnyUnordered = false; + + DbTxn* ptxn = dbenv.TxnBegin(); + BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) + { + if (fOnlyKeys) + { + CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); + CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); + string strType, strErr; + bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + nFileVersion, vWalletUpgrade, + fIsEncrypted, fAnyUnordered, + strType, strErr); + if (!IsKeyType(strType)) + continue; + if (!fReadOK) + { + printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); + continue; + } + } + Dbt datKey(&row.first[0], row.first.size()); + Dbt datValue(&row.second[0], row.second.size()); + int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + ptxn->commit(0); + pdbCopy->close(0); + delete pdbCopy; + + return fSuccess; +} + +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) +{ + return CWalletDB::Recover(dbenv, filename, false); +}