Skip to content

Commit 9ea2d25

Browse files
committed
Move RecoverDatabaseFile and RecoverKeysOnlyFilter into salvage.{cpp/h}
1 parent b426c77 commit 9ea2d25

File tree

8 files changed

+180
-155
lines changed

8 files changed

+180
-155
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ BITCOIN_CORE_H = \
243243
wallet/ismine.h \
244244
wallet/load.h \
245245
wallet/rpcwallet.h \
246+
wallet/salvage.h \
246247
wallet/scriptpubkeyman.h \
247248
wallet/wallet.h \
248249
wallet/walletdb.h \
@@ -351,6 +352,7 @@ libbitcoin_wallet_a_SOURCES = \
351352
wallet/load.cpp \
352353
wallet/rpcdump.cpp \
353354
wallet/rpcwallet.cpp \
355+
wallet/salvage.cpp \
354356
wallet/scriptpubkeyman.cpp \
355357
wallet/wallet.cpp \
356358
wallet/walletdb.cpp \

src/wallet/db.cpp

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -317,134 +317,6 @@ BerkeleyBatch::SafeDbt::operator Dbt*()
317317
return &m_dbt;
318318
}
319319

320-
/* End of headers, beginning of key/value data */
321-
static const char *HEADER_END = "HEADER=END";
322-
/* End of key/value data */
323-
static const char *DATA_END = "DATA=END";
324-
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
325-
326-
bool RecoverDatabaseFile(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
327-
{
328-
std::string filename;
329-
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
330-
331-
// Recovery procedure:
332-
// move wallet file to walletfilename.timestamp.bak
333-
// Call Salvage with fAggressive=true to
334-
// get as much data as possible.
335-
// Rewrite salvaged data to fresh wallet file
336-
// Set -rescan so any missing transactions will be
337-
// found.
338-
int64_t now = GetTime();
339-
newFilename = strprintf("%s.%d.bak", filename, now);
340-
341-
int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
342-
newFilename.c_str(), DB_AUTO_COMMIT);
343-
if (result == 0)
344-
LogPrintf("Renamed %s to %s\n", filename, newFilename);
345-
else
346-
{
347-
LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
348-
return false;
349-
}
350-
351-
/**
352-
* Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
353-
* key/value pairs are appended to salvagedData which are then written out to a new wallet file.
354-
* NOTE: reads the entire database into memory, so cannot be used
355-
* for huge databases.
356-
*/
357-
std::vector<KeyValPair> salvagedData;
358-
359-
std::stringstream strDump;
360-
361-
Db db(env->dbenv.get(), 0);
362-
result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
363-
if (result == DB_VERIFY_BAD) {
364-
LogPrintf("Salvage: Database salvage found errors, all data may not be recoverable.\n");
365-
}
366-
if (result != 0 && result != DB_VERIFY_BAD) {
367-
LogPrintf("Salvage: Database salvage failed with result %d.\n", result);
368-
return false;
369-
}
370-
371-
// Format of bdb dump is ascii lines:
372-
// header lines...
373-
// HEADER=END
374-
// hexadecimal key
375-
// hexadecimal value
376-
// ... repeated
377-
// DATA=END
378-
379-
std::string strLine;
380-
while (!strDump.eof() && strLine != HEADER_END)
381-
getline(strDump, strLine); // Skip past header
382-
383-
std::string keyHex, valueHex;
384-
while (!strDump.eof() && keyHex != DATA_END) {
385-
getline(strDump, keyHex);
386-
if (keyHex != DATA_END) {
387-
if (strDump.eof())
388-
break;
389-
getline(strDump, valueHex);
390-
if (valueHex == DATA_END) {
391-
LogPrintf("Salvage: WARNING: Number of keys in data does not match number of values.\n");
392-
break;
393-
}
394-
salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
395-
}
396-
}
397-
398-
bool fSuccess;
399-
if (keyHex != DATA_END) {
400-
LogPrintf("Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
401-
fSuccess = false;
402-
} else {
403-
fSuccess = (result == 0);
404-
}
405-
406-
if (salvagedData.empty())
407-
{
408-
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
409-
return false;
410-
}
411-
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
412-
413-
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
414-
int ret = pdbCopy->open(nullptr, // Txn pointer
415-
filename.c_str(), // Filename
416-
"main", // Logical db name
417-
DB_BTREE, // Database type
418-
DB_CREATE, // Flags
419-
0);
420-
if (ret > 0) {
421-
LogPrintf("Cannot create database file %s\n", filename);
422-
pdbCopy->close(0);
423-
return false;
424-
}
425-
426-
DbTxn* ptxn = env->TxnBegin();
427-
for (KeyValPair& row : salvagedData)
428-
{
429-
if (recoverKVcallback)
430-
{
431-
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
432-
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
433-
if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
434-
continue;
435-
}
436-
Dbt datKey(&row.first[0], row.first.size());
437-
Dbt datValue(&row.second[0], row.second.size());
438-
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
439-
if (ret2 > 0)
440-
fSuccess = false;
441-
}
442-
ptxn->commit(0);
443-
pdbCopy->close(0);
444-
445-
return fSuccess;
446-
}
447-
448320
bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
449321
{
450322
std::string walletFile;

src/wallet/db.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,6 @@ class BerkeleyBatch
389389
bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
390390
};
391391

392-
bool RecoverDatabaseFile(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
393-
394392
std::string BerkeleyDatabaseVersion();
395393

396394
#endif // BITCOIN_WALLET_DB_H

src/wallet/salvage.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2020 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#include <fs.h>
7+
#include <streams.h>
8+
#include <wallet/salvage.h>
9+
#include <wallet/wallet.h>
10+
#include <wallet/walletdb.h>
11+
12+
/* End of headers, beginning of key/value data */
13+
static const char *HEADER_END = "HEADER=END";
14+
/* End of key/value data */
15+
static const char *DATA_END = "DATA=END";
16+
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
17+
18+
bool RecoverDatabaseFile(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
19+
{
20+
std::string filename;
21+
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
22+
23+
// Recovery procedure:
24+
// move wallet file to walletfilename.timestamp.bak
25+
// Call Salvage with fAggressive=true to
26+
// get as much data as possible.
27+
// Rewrite salvaged data to fresh wallet file
28+
// Set -rescan so any missing transactions will be
29+
// found.
30+
int64_t now = GetTime();
31+
newFilename = strprintf("%s.%d.bak", filename, now);
32+
33+
int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
34+
newFilename.c_str(), DB_AUTO_COMMIT);
35+
if (result == 0)
36+
LogPrintf("Renamed %s to %s\n", filename, newFilename);
37+
else
38+
{
39+
LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
40+
return false;
41+
}
42+
43+
/**
44+
* Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
45+
* key/value pairs are appended to salvagedData which are then written out to a new wallet file.
46+
* NOTE: reads the entire database into memory, so cannot be used
47+
* for huge databases.
48+
*/
49+
std::vector<KeyValPair> salvagedData;
50+
51+
std::stringstream strDump;
52+
53+
Db db(env->dbenv.get(), 0);
54+
result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
55+
if (result == DB_VERIFY_BAD) {
56+
LogPrintf("Salvage: Database salvage found errors, all data may not be recoverable.\n");
57+
}
58+
if (result != 0 && result != DB_VERIFY_BAD) {
59+
LogPrintf("Salvage: Database salvage failed with result %d.\n", result);
60+
return false;
61+
}
62+
63+
// Format of bdb dump is ascii lines:
64+
// header lines...
65+
// HEADER=END
66+
// hexadecimal key
67+
// hexadecimal value
68+
// ... repeated
69+
// DATA=END
70+
71+
std::string strLine;
72+
while (!strDump.eof() && strLine != HEADER_END)
73+
getline(strDump, strLine); // Skip past header
74+
75+
std::string keyHex, valueHex;
76+
while (!strDump.eof() && keyHex != DATA_END) {
77+
getline(strDump, keyHex);
78+
if (keyHex != DATA_END) {
79+
if (strDump.eof())
80+
break;
81+
getline(strDump, valueHex);
82+
if (valueHex == DATA_END) {
83+
LogPrintf("Salvage: WARNING: Number of keys in data does not match number of values.\n");
84+
break;
85+
}
86+
salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
87+
}
88+
}
89+
90+
bool fSuccess;
91+
if (keyHex != DATA_END) {
92+
LogPrintf("Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
93+
fSuccess = false;
94+
} else {
95+
fSuccess = (result == 0);
96+
}
97+
98+
if (salvagedData.empty())
99+
{
100+
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
101+
return false;
102+
}
103+
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
104+
105+
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
106+
int ret = pdbCopy->open(nullptr, // Txn pointer
107+
filename.c_str(), // Filename
108+
"main", // Logical db name
109+
DB_BTREE, // Database type
110+
DB_CREATE, // Flags
111+
0);
112+
if (ret > 0) {
113+
LogPrintf("Cannot create database file %s\n", filename);
114+
pdbCopy->close(0);
115+
return false;
116+
}
117+
118+
DbTxn* ptxn = env->TxnBegin();
119+
for (KeyValPair& row : salvagedData)
120+
{
121+
if (recoverKVcallback)
122+
{
123+
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
124+
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
125+
if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
126+
continue;
127+
}
128+
Dbt datKey(&row.first[0], row.first.size());
129+
Dbt datValue(&row.second[0], row.second.size());
130+
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
131+
if (ret2 > 0)
132+
fSuccess = false;
133+
}
134+
ptxn->commit(0);
135+
pdbCopy->close(0);
136+
137+
return fSuccess;
138+
}
139+
140+
bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
141+
{
142+
CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
143+
std::string strType, strErr;
144+
bool fReadOK;
145+
{
146+
// Required in LoadKeyMetadata():
147+
LOCK(dummyWallet->cs_wallet);
148+
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue, strType, strErr);
149+
}
150+
if (!WalletBatch::IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
151+
return false;
152+
}
153+
if (!fReadOK)
154+
{
155+
LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
156+
return false;
157+
}
158+
159+
return true;
160+
}

src/wallet/salvage.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2020 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#ifndef BITCOIN_WALLET_SALVAGE_H
7+
#define BITCOIN_WALLET_SALVAGE_H
8+
9+
#include <fs.h>
10+
#include <streams.h>
11+
12+
bool RecoverDatabaseFile(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
13+
14+
/* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
15+
bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
16+
17+
#endif // BITCOIN_WALLET_SALVAGE_H

src/wallet/walletdb.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -885,28 +885,6 @@ void MaybeCompactWalletDB()
885885
fOneThread = false;
886886
}
887887

888-
bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
889-
{
890-
CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
891-
std::string strType, strErr;
892-
bool fReadOK;
893-
{
894-
// Required in LoadKeyMetadata():
895-
LOCK(dummyWallet->cs_wallet);
896-
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue, strType, strErr);
897-
}
898-
if (!WalletBatch::IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
899-
return false;
900-
}
901-
if (!fReadOK)
902-
{
903-
LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
904-
return false;
905-
}
906-
907-
return true;
908-
}
909-
910888
bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, bilingual_str& errorStr)
911889
{
912890
return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);

src/wallet/walletdb.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,4 @@ void MaybeCompactWalletDB();
291291
//! Unserialize a given Key-Value pair and load it into the wallet
292292
bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr);
293293

294-
/* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
295-
bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
296-
297294
#endif // BITCOIN_WALLET_WALLETDB_H

src/wallet/wallettool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <fs.h>
66
#include <util/system.h>
77
#include <util/translation.h>
8+
#include <wallet/salvage.h>
89
#include <wallet/wallet.h>
910
#include <wallet/walletutil.h>
1011

0 commit comments

Comments
 (0)