mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 15:50:07 +01:00
Handle corrupt wallets gracefully.
Corrupt wallets used to cause a DB_RUNRECOVERY uncaught exception and a
crash. This commit does three things:
1) Runs a BDB verify early in the startup process, and if there is a
low-level problem with the database:
+ Moves the bad wallet.dat to wallet.timestamp.bak
+ Runs a 'salvage' operation to get key/value pairs, and
writes them to a new wallet.dat
+ Continues with startup.
2) Much more tolerant of serialization errors. All errors in deserialization
are reported by tolerated EXCEPT for errors related to reading keypairs
or master key records-- those are reported and then shut down, so the user
can get help (or recover from a backup).
3) Adds a new -salvagewallet option, which:
+ Moves the wallet.dat to wallet.timestamp.bak
+ extracts ONLY keypairs and master keys into a new wallet.dat
+ soft-sets -rescan, to recreate transaction history
This was tested by randomly corrupting testnet wallets using a little
python script I wrote (https://gist.github.com/3812689)
This commit is contained in:
72
src/db.cpp
72
src/db.cpp
@@ -136,6 +136,69 @@ void CDBEnv::MakeMock()
|
||||
fMockDb = true;
|
||||
}
|
||||
|
||||
CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
|
||||
{
|
||||
LOCK(cs_db);
|
||||
assert(mapFileUseCount.count(strFile) == 0);
|
||||
|
||||
Db db(&dbenv, 0);
|
||||
int result = db.verify(strFile.c_str(), NULL, NULL, 0);
|
||||
if (result == 0)
|
||||
return VERIFY_OK;
|
||||
else if (recoverFunc == NULL)
|
||||
return RECOVER_FAIL;
|
||||
|
||||
// Try to recover:
|
||||
bool fRecovered = (*recoverFunc)(*this, strFile);
|
||||
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
|
||||
}
|
||||
|
||||
bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
|
||||
std::vector<CDBEnv::KeyValPair >& vResult)
|
||||
{
|
||||
LOCK(cs_db);
|
||||
assert(mapFileUseCount.count(strFile) == 0);
|
||||
|
||||
u_int32_t flags = DB_SALVAGE;
|
||||
if (fAggressive) flags |= DB_AGGRESSIVE;
|
||||
|
||||
stringstream strDump;
|
||||
|
||||
Db db(&dbenv, 0);
|
||||
int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
|
||||
if (result != 0)
|
||||
{
|
||||
printf("ERROR: db salvage failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format of bdb dump is ascii lines:
|
||||
// header lines...
|
||||
// HEADER=END
|
||||
// hexadecimal key
|
||||
// hexadecimal value
|
||||
// ... repeated
|
||||
// DATA=END
|
||||
|
||||
string strLine;
|
||||
while (!strDump.eof() && strLine != "HEADER=END")
|
||||
getline(strDump, strLine); // Skip past header
|
||||
|
||||
std::string keyHex, valueHex;
|
||||
while (!strDump.eof() && keyHex != "DATA=END")
|
||||
{
|
||||
getline(strDump, keyHex);
|
||||
if (keyHex != "DATA_END")
|
||||
{
|
||||
getline(strDump, valueHex);
|
||||
vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
|
||||
}
|
||||
}
|
||||
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
|
||||
void CDBEnv::CheckpointLSN(std::string strFile)
|
||||
{
|
||||
dbenv.txn_checkpoint(0, 0, 0);
|
||||
@@ -257,6 +320,15 @@ void CDBEnv::CloseDb(const string& strFile)
|
||||
}
|
||||
}
|
||||
|
||||
bool CDBEnv::RemoveDb(const string& strFile)
|
||||
{
|
||||
this->CloseDb(strFile);
|
||||
|
||||
LOCK(cs_db);
|
||||
int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
|
||||
return (rc == 0);
|
||||
}
|
||||
|
||||
bool CDB::Rewrite(const string& strFile, const char* pszSkip)
|
||||
{
|
||||
while (!fShutdown)
|
||||
|
||||
Reference in New Issue
Block a user