Skip to content

Commit c16390d

Browse files
committed
[Wallet] refactor CWallet/CWalletDB/CDB (Round 1)
Try to hide CDB/bitdb behinde CWalletDB. Prepare for full wallet database abstraction. An adapted version of btc@7184e25c80aa8b1629a700bb7a7e290ad0bb2792
1 parent 99adac0 commit c16390d

File tree

6 files changed

+190
-109
lines changed

6 files changed

+190
-109
lines changed

src/guiinterfaceutil.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef GUIINTERFACEUTIL_H
66
#define GUIINTERFACEUTIL_H
77

8+
#include "guiinterface.h"
9+
810
inline static bool UIError(const std::string &str)
911
{
1012
uiInterface.ThreadSafeMessageBox(str, "Error", CClientUIInterface::MSG_ERROR);

src/wallet/db.cpp

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "db.h"
88

99
#include "addrman.h"
10+
#include "guiinterfaceutil.h"
1011
#include "hash.h"
1112
#include "protocol.h"
1213
#include "util.h"
@@ -144,7 +145,7 @@ void CDBEnv::MakeMock()
144145
fMockDb = true;
145146
}
146147

147-
CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFunc)(CDBEnv& dbenv, const std::string& strFile))
148+
CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile))
148149
{
149150
LOCK(cs_db);
150151
assert(mapFileUseCount.count(strFile) == 0);
@@ -157,10 +158,123 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu
157158
return RECOVER_FAIL;
158159

159160
// Try to recover:
160-
bool fRecovered = (*recoverFunc)(*this, strFile);
161+
bool fRecovered = (*recoverFunc)(strFile);
161162
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
162163
}
163164

165+
bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue))
166+
{
167+
// Recovery procedure:
168+
// move wallet file to wallet.timestamp.bak
169+
// Call Salvage with fAggressive=true to
170+
// get as much data as possible.
171+
// Rewrite salvaged data to fresh wallet file
172+
// Set -rescan so any missing transactions will be
173+
// found.
174+
int64_t now = GetTime();
175+
std::string newFilename = strprintf("wallet.%d.bak", now);
176+
177+
int result = bitdb.dbenv->dbrename(NULL, filename.c_str(), NULL,
178+
newFilename.c_str(), DB_AUTO_COMMIT);
179+
if (result == 0) {
180+
LogPrintf("Renamed %s to %s\n", filename, newFilename);
181+
} else {
182+
LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
183+
return false;
184+
}
185+
186+
std::vector<CDBEnv::KeyValPair> salvagedData;
187+
bool fSuccess = bitdb.Salvage(newFilename, true, salvagedData);
188+
if (salvagedData.empty()) {
189+
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
190+
return false;
191+
}
192+
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
193+
194+
std::unique_ptr<Db> pdbCopy(new Db(bitdb.dbenv, 0));
195+
int ret = pdbCopy->open(NULL, // Txn pointer
196+
filename.c_str(), // Filename
197+
"main", // Logical db name
198+
DB_BTREE, // Database type
199+
DB_CREATE, // Flags
200+
0);
201+
if (ret > 0) {
202+
LogPrintf("Cannot create database file %s\n", filename);
203+
return false;
204+
}
205+
206+
DbTxn* ptxn = bitdb.TxnBegin();
207+
for (CDBEnv::KeyValPair& row : salvagedData) {
208+
if (recoverKVcallback) {
209+
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
210+
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
211+
std::string strType, strErr;
212+
if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
213+
continue;
214+
}
215+
Dbt datKey(&row.first[0], row.first.size());
216+
Dbt datValue(&row.second[0], row.second.size());
217+
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
218+
if (ret2 > 0)
219+
fSuccess = false;
220+
}
221+
ptxn->commit(0);
222+
pdbCopy->close(0);
223+
224+
return fSuccess;
225+
}
226+
227+
bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
228+
{
229+
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
230+
LogPrintf("Using wallet %s\n", walletFile);
231+
232+
// Wallet file must be a plain filename without a directory
233+
fs::path wallet_file_path(walletFile);
234+
if (walletFile != wallet_file_path.filename().string())
235+
return UIError(strprintf(("Wallet %s resides outside data directory %s"), walletFile, dataDir.string()));
236+
237+
if (!bitdb.Open(dataDir)) {
238+
// try moving the database env out of the way
239+
fs::path pathDatabase = dataDir / "database";
240+
fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime());
241+
try {
242+
fs::rename(pathDatabase, pathDatabaseBak);
243+
LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
244+
} catch (const fs::filesystem_error&) {
245+
// failure is ok (well, not really, but it's not worse than what we started with)
246+
}
247+
248+
// try again
249+
if (!bitdb.Open(dataDir)) {
250+
// if it still fails, it probably means we can't even create the database env
251+
errorStr = strprintf(("Error initializing wallet database environment %s!"), GetDataDir());
252+
return false;
253+
}
254+
}
255+
return true;
256+
}
257+
258+
bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
259+
{
260+
if (fs::exists(dataDir / walletFile)) {
261+
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc);
262+
if (r == CDBEnv::RECOVER_OK) {
263+
warningStr = strprintf(("Warning: Wallet file corrupt, data salvaged!"
264+
" Original %s saved as %s in %s; if"
265+
" your balance or transactions are incorrect you should"
266+
" restore from a backup."),
267+
walletFile, "wallet.{timestamp}.bak", dataDir);
268+
}
269+
if (r == CDBEnv::RECOVER_FAIL) {
270+
errorStr = strprintf(("%s corrupt, salvage failed"), walletFile);
271+
return false;
272+
}
273+
}
274+
// also return true if files does not exists
275+
return true;
276+
}
277+
164278
/* End of headers, beginning of key/value data */
165279
static const char *HEADER_END = "HEADER=END";
166280
/* End of key/value data */

src/wallet/db.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class CDBEnv
5858
enum VerifyResult { VERIFY_OK,
5959
RECOVER_OK,
6060
RECOVER_FAIL };
61-
VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(CDBEnv& dbenv, const std::string& strFile));
61+
VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile));
6262
/**
6363
* Salvage data from a file that Verify says is bad.
6464
* fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
@@ -106,6 +106,16 @@ class CDB
106106
public:
107107
void Flush();
108108
void Close();
109+
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
110+
111+
/* flush the wallet passively (TRY_LOCK)
112+
ideal to be called periodically */
113+
// TODO: Back port Periodic flush..
114+
//static bool PeriodicFlush(std::string strFile);
115+
/* verifies the database environment */
116+
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
117+
/* verifies the database file */
118+
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
109119

110120
private:
111121
CDB(const CDB&);

src/wallet/wallet.cpp

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,54 +1790,30 @@ std::set<uint256> CWalletTx::GetConflicts() const
17901790

17911791
bool CWallet::Verify()
17921792
{
1793+
uiInterface.InitMessage(_("Verifying wallet..."));
17931794
std::string walletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT);
17941795
std::string strDataDir = GetDataDir().string();
17951796

1796-
// Wallet file must be a plain filename without a directory
1797-
fs::path wallet_file_path(walletFile);
1798-
if (walletFile != wallet_file_path.filename().string())
1799-
return UIError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, strDataDir));
1800-
1801-
LogPrintf("Using wallet %s\n", walletFile);
1802-
uiInterface.InitMessage(_("Verifying wallet..."));
1803-
1804-
if (!bitdb.Open(GetDataDir())) {
1805-
// try moving the database env out of the way
1806-
fs::path pathDatabase = GetDataDir() / "database";
1807-
fs::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime());
1808-
try {
1809-
fs::rename(pathDatabase, pathDatabaseBak);
1810-
LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
1811-
} catch (const fs::filesystem_error& error) {
1812-
// failure is ok (well, not really, but it's not worse than what we started with)
1813-
}
1814-
1815-
// try again
1816-
if (!bitdb.Open(GetDataDir())) {
1817-
// if it still fails, it probably means we can't even create the database env
1818-
return UIError(strprintf(_("Error initializing wallet database environment %s!"), strDataDir));
1819-
}
1820-
}
1797+
std::string strError;
1798+
if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError))
1799+
return UIError(strError);
18211800

18221801
if (gArgs.GetBoolArg("-salvagewallet", false)) {
18231802
// Recover readable keypairs:
1824-
if (!CWalletDB::Recover(bitdb, walletFile, true))
1803+
CWallet dummyWallet;
1804+
if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter))
18251805
return false;
18261806
}
18271807

1828-
if (fs::exists(GetDataDir() / walletFile)) {
1829-
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover);
1830-
if (r == CDBEnv::RECOVER_OK) {
1831-
UIWarning(strprintf(_("Warning: wallet file corrupt, data salvaged!"
1832-
" Original %s saved as %s in %s; if"
1833-
" your balance or transactions are incorrect you should"
1834-
" restore from a backup."),
1835-
walletFile, "wallet.{timestamp}.bak", strDataDir));
1836-
}
1837-
if (r == CDBEnv::RECOVER_FAIL)
1838-
return UIError(strprintf(_("%s corrupt, salvage failed"), walletFile));
1808+
std::string strWarning;
1809+
bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError);
1810+
if (!strWarning.empty()) {
1811+
UIWarning(strWarning);
1812+
}
1813+
if (!dbV) {
1814+
UIError(strError);
1815+
return false;
18391816
}
1840-
18411817
return true;
18421818
}
18431819

src/wallet/walletdb.cpp

Lines changed: 35 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW
701701
return true;
702702
}
703703

704-
static bool IsKeyType(std::string strType)
704+
bool CWalletDB::IsKeyType(const std::string& strType)
705705
{
706706
return (strType == "key" || strType == "wkey" ||
707707
strType == "mkey" || strType == "ckey" ||
@@ -1079,80 +1079,49 @@ bool AttemptBackupWallet(const CWallet& wallet, const fs::path& pathSrc, const f
10791079
//
10801080
// Try to (very carefully!) recover wallet file if there is a problem.
10811081
//
1082-
bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys)
1083-
{
1084-
// Recovery procedure:
1085-
// move wallet file to wallet.timestamp.bak
1086-
// Call Salvage with fAggressive=true to
1087-
// get as much data as possible.
1088-
// Rewrite salvaged data to fresh wallet file.
1089-
// Set -rescan so any missing transactions will be
1090-
// found.
1091-
int64_t now = GetTime();
1092-
std::string newFilename = strprintf("wallet.%d.bak", now);
1093-
1094-
int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL,
1095-
newFilename.c_str(), DB_AUTO_COMMIT);
1096-
if (result == 0)
1097-
LogPrintf("Renamed %s to %s\n", filename, newFilename);
1098-
else {
1099-
LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
1100-
return false;
1101-
}
1082+
bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue))
1083+
{
1084+
return CDB::Recover(filename, callbackDataIn, recoverKVcallback);
1085+
}
11021086

1103-
std::vector<CDBEnv::KeyValPair> salvagedData;
1104-
bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
1105-
if (salvagedData.empty()) {
1106-
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
1107-
return false;
1087+
bool CWalletDB::Recover(const std::string& filename)
1088+
{
1089+
// recover without a key filter callback
1090+
// results in recovering all record types
1091+
return CWalletDB::Recover(filename, NULL, NULL);
1092+
}
1093+
1094+
bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
1095+
{
1096+
CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
1097+
CWalletScanState dummyWss;
1098+
std::string strType, strErr;
1099+
bool fReadOK;
1100+
{
1101+
// Required in LoadKeyMetadata():
1102+
LOCK(dummyWallet->cs_wallet);
1103+
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
1104+
dummyWss, strType, strErr);
11081105
}
1109-
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
1110-
1111-
bool fSuccess = allOK;
1112-
std::unique_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0));
1113-
int ret = pdbCopy->open(NULL, // Txn pointer
1114-
filename.c_str(), // Filename
1115-
"main", // Logical db name
1116-
DB_BTREE, // Database type
1117-
DB_CREATE, // Flags
1118-
0);
1119-
if (ret > 0) {
1120-
LogPrintf("Cannot create database file %s\n", filename);
1106+
if (!IsKeyType(strType) && strType != "hdchain")
1107+
return false;
1108+
if (!fReadOK)
1109+
{
1110+
LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
11211111
return false;
11221112
}
1123-
CWallet dummyWallet;
1124-
CWalletScanState wss;
11251113

1126-
DbTxn* ptxn = dbenv.TxnBegin();
1127-
for (CDBEnv::KeyValPair& row : salvagedData) {
1128-
if (fOnlyKeys) {
1129-
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
1130-
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
1131-
std::string strType, strErr;
1132-
bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
1133-
wss, strType, strErr);
1134-
if (!IsKeyType(strType))
1135-
continue;
1136-
if (!fReadOK) {
1137-
LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
1138-
continue;
1139-
}
1140-
}
1141-
Dbt datKey(&row.first[0], row.first.size());
1142-
Dbt datValue(&row.second[0], row.second.size());
1143-
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
1144-
if (ret2 > 0)
1145-
fSuccess = false;
1146-
}
1147-
ptxn->commit(0);
1148-
pdbCopy->close(0);
1114+
return true;
1115+
}
11491116

1150-
return fSuccess;
1117+
bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
1118+
{
1119+
return CDB::VerifyEnvironment(walletFile, dataDir, errorStr);
11511120
}
11521121

1153-
bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename)
1122+
bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr)
11541123
{
1155-
return CWalletDB::Recover(dbenv, filename, false);
1124+
return CDB::VerifyDatabaseFile(walletFile, dataDir, warningStr, errorStr, CWalletDB::Recover);
11561125
}
11571126

11581127
bool CWalletDB::WriteDestData(const std::string& address, const std::string& key, const std::string& value)

src/wallet/walletdb.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,18 @@ class CWalletDB : public CDB
175175
DBErrors LoadWallet(CWallet* pwallet);
176176
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
177177
DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx);
178-
static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys);
179-
static bool Recover(CDBEnv& dbenv, const std::string& filename);
178+
/* Try to (very carefully!) recover wallet database (with a possible key type filter) */
179+
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
180+
/* Recover convenience-function to bypass the key filter callback, called when verify failes, recoveres everything */
181+
static bool Recover(const std::string& filename);
182+
/* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
183+
static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
184+
/* Function to determin if a certain KV/key-type is a key (cryptographical key) type */
185+
static bool IsKeyType(const std::string& strType);
186+
/* verifies the database environment */
187+
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
188+
/* verifies the database file */
189+
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr);
180190

181191
bool WriteDeterministicMint(const CDeterministicMint& dMint);
182192
bool ReadDeterministicMint(const uint256& hashPubcoin, CDeterministicMint& dMint);

0 commit comments

Comments
 (0)