Bitcoin Core 28.99.0
P2P Digital Currency
salvage.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2021 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 <streams.h>
7#include <util/fs.h>
8#include <util/translation.h>
9#include <wallet/bdb.h>
10#include <wallet/salvage.h>
11#include <wallet/wallet.h>
12#include <wallet/walletdb.h>
13
14#include <db_cxx.h>
15
16namespace wallet {
17/* End of headers, beginning of key/value data */
18static const char *HEADER_END = "HEADER=END";
19/* End of key/value data */
20static const char *DATA_END = "DATA=END";
21typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
22
24{
25 Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
26};
27
30{
31private:
32 bool ReadKey(DataStream&& key, DataStream& value) override { return true; }
33 bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override { return true; }
34 bool EraseKey(DataStream&& key) override { return true; }
35 bool HasKey(DataStream&& key) override { return true; }
36 bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
37
38public:
39 void Flush() override {}
40 void Close() override {}
41
42 std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
43 std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(Span<const std::byte> prefix) override { return GetNewCursor(); }
44 bool TxnBegin() override { return true; }
45 bool TxnCommit() override { return true; }
46 bool TxnAbort() override { return true; }
47 bool HasActiveTxn() override { return false; }
48};
49
53{
54public:
55 void Open() override {};
56 void AddRef() override {}
57 void RemoveRef() override {}
58 bool Rewrite(const char* pszSkip=nullptr) override { return true; }
59 bool Backup(const std::string& strDest) const override { return true; }
60 void Close() override {}
61 void Flush() override {}
62 bool PeriodicFlush() override { return true; }
64 void ReloadDbEnv() override {}
65 std::string Filename() override { return "dummy"; }
66 std::string Format() override { return "dummy"; }
67 std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
68};
69
70bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
71{
72 DatabaseOptions options;
73 DatabaseStatus status;
74 ReadDatabaseArgs(args, options);
75 options.require_existing = true;
76 options.verify = false;
78 std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
79 if (!database) return false;
80
81 BerkeleyDatabase& berkeley_database = static_cast<BerkeleyDatabase&>(*database);
82 std::string filename = berkeley_database.Filename();
83 std::shared_ptr<BerkeleyEnvironment> env = berkeley_database.env;
84
85 if (!env->Open(error)) {
86 return false;
87 }
88
89 // Recovery procedure:
90 // move wallet file to walletfilename.timestamp.bak
91 // Call Salvage with fAggressive=true to
92 // get as much data as possible.
93 // Rewrite salvaged data to fresh wallet file
94 // Rescan so any missing transactions will be
95 // found.
96 int64_t now = GetTime();
97 std::string newFilename = strprintf("%s.%d.bak", filename, now);
98
99 int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
100 newFilename.c_str(), DB_AUTO_COMMIT);
101 if (result != 0)
102 {
103 error = Untranslated(strprintf("Failed to rename %s to %s", filename, newFilename));
104 return false;
105 }
106
113 std::vector<KeyValPair> salvagedData;
114
115 std::stringstream strDump;
116
117 Db db(env->dbenv.get(), 0);
118 result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
119 if (result == DB_VERIFY_BAD) {
120 warnings.emplace_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable."));
121 }
122 if (result != 0 && result != DB_VERIFY_BAD) {
123 error = Untranslated(strprintf("Salvage: Database salvage failed with result %d.", result));
124 return false;
125 }
126
127 // Format of bdb dump is ascii lines:
128 // header lines...
129 // HEADER=END
130 // hexadecimal key
131 // hexadecimal value
132 // ... repeated
133 // DATA=END
134
135 std::string strLine;
136 while (!strDump.eof() && strLine != HEADER_END)
137 getline(strDump, strLine); // Skip past header
138
139 std::string keyHex, valueHex;
140 while (!strDump.eof() && keyHex != DATA_END) {
141 getline(strDump, keyHex);
142 if (keyHex != DATA_END) {
143 if (strDump.eof())
144 break;
145 getline(strDump, valueHex);
146 if (valueHex == DATA_END) {
147 warnings.emplace_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values."));
148 break;
149 }
150 salvagedData.emplace_back(ParseHex(keyHex), ParseHex(valueHex));
151 }
152 }
153
154 bool fSuccess;
155 if (keyHex != DATA_END) {
156 warnings.emplace_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output."));
157 fSuccess = false;
158 } else {
159 fSuccess = (result == 0);
160 }
161
162 if (salvagedData.empty())
163 {
164 error = Untranslated(strprintf("Salvage(aggressive) found no records in %s.", newFilename));
165 return false;
166 }
167
168 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
169 int ret = pdbCopy->open(nullptr, // Txn pointer
170 filename.c_str(), // Filename
171 "main", // Logical db name
172 DB_BTREE, // Database type
173 DB_CREATE, // Flags
174 0);
175 if (ret > 0) {
176 error = Untranslated(strprintf("Cannot create database file %s", filename));
177 pdbCopy->close(0);
178 return false;
179 }
180
181 DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
182 CWallet dummyWallet(nullptr, "", std::make_unique<DummyDatabase>());
183 for (KeyValPair& row : salvagedData)
184 {
185 /* Filter for only private key type KV pairs to be added to the salvaged wallet */
186 DataStream ssKey{row.first};
187 DataStream ssValue(row.second);
188 std::string strType, strErr;
189
190 // We only care about KEY, MASTER_KEY, CRYPTED_KEY, and HDCHAIN types
191 ssKey >> strType;
192 bool fReadOK = false;
193 if (strType == DBKeys::KEY) {
194 fReadOK = LoadKey(&dummyWallet, ssKey, ssValue, strErr);
195 } else if (strType == DBKeys::CRYPTED_KEY) {
196 fReadOK = LoadCryptedKey(&dummyWallet, ssKey, ssValue, strErr);
197 } else if (strType == DBKeys::MASTER_KEY) {
198 fReadOK = LoadEncryptionKey(&dummyWallet, ssKey, ssValue, strErr);
199 } else if (strType == DBKeys::HDCHAIN) {
200 fReadOK = LoadHDChain(&dummyWallet, ssValue, strErr);
201 } else {
202 continue;
203 }
204
205 if (!fReadOK)
206 {
207 warnings.push_back(Untranslated(strprintf("WARNING: WalletBatch::Recover skipping %s: %s", strType, strErr)));
208 continue;
209 }
210 Dbt datKey(row.first.data(), row.first.size());
211 Dbt datValue(row.second.data(), row.second.size());
212 int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
213 if (ret2 > 0)
214 fSuccess = false;
215 }
216 ptxn->commit(0);
217 pdbCopy->close(0);
218
219 return fSuccess;
220}
221} // namespace wallet
int ret
ArgsManager & args
Definition: bitcoind.cpp:277
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
An instance of this class represents one database.
Definition: bdb.h:89
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: bdb.h:145
std::string Filename() override
Return path to main database filename.
Definition: bdb.h:133
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:300
RAII class that provides access to a WalletDatabase.
Definition: db.h:51
RAII class that provides access to a DummyDatabase.
Definition: salvage.cpp:30
bool EraseKey(DataStream &&key) override
Definition: salvage.cpp:34
bool HasActiveTxn() override
Definition: salvage.cpp:47
bool HasKey(DataStream &&key) override
Definition: salvage.cpp:35
bool TxnAbort() override
Definition: salvage.cpp:46
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
Definition: salvage.cpp:43
void Close() override
Definition: salvage.cpp:40
bool ReadKey(DataStream &&key, DataStream &value) override
Definition: salvage.cpp:32
bool ErasePrefix(Span< const std::byte > prefix) override
Definition: salvage.cpp:36
std::unique_ptr< DatabaseCursor > GetNewCursor() override
Definition: salvage.cpp:42
bool TxnCommit() override
Definition: salvage.cpp:45
void Flush() override
Definition: salvage.cpp:39
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
Definition: salvage.cpp:33
bool TxnBegin() override
Definition: salvage.cpp:44
Status Next(DataStream &key, DataStream &value) override
Definition: salvage.cpp:25
A dummy WalletDatabase that does nothing and never fails.
Definition: salvage.cpp:53
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
Definition: salvage.cpp:58
void AddRef() override
Indicate the a new database user has began using the database.
Definition: salvage.cpp:56
void ReloadDbEnv() override
Definition: salvage.cpp:64
bool PeriodicFlush() override
Definition: salvage.cpp:62
void IncrementUpdateCounter() override
Definition: salvage.cpp:63
void Flush() override
Make sure all changes are flushed to database file.
Definition: salvage.cpp:61
std::string Filename() override
Return path to main database file for logs and error messages.
Definition: salvage.cpp:65
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
Definition: salvage.cpp:67
void Open() override
Open the database if it is not already opened.
Definition: salvage.cpp:55
std::string Format() override
Definition: salvage.cpp:66
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
Definition: salvage.cpp:57
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Definition: salvage.cpp:59
void Close() override
Flush to the database file and close the database.
Definition: salvage.cpp:60
An instance of this class represents one database.
Definition: db.h:131
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:175
const std::string KEY
Definition: walletdb.cpp:48
const std::string CRYPTED_KEY
Definition: walletdb.cpp:41
const std::string HDCHAIN
Definition: walletdb.cpp:46
const std::string MASTER_KEY
Definition: walletdb.cpp:50
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition: db.cpp:154
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1380
bool LoadKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:316
bool LoadCryptedKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:382
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Definition: salvage.cpp:21
bool LoadEncryptionKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:421
bool LoadHDChain(CWallet *pwallet, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:448
static const char * HEADER_END
Definition: salvage.cpp:18
static const char * DATA_END
Definition: salvage.cpp:20
bool RecoverDatabaseFile(const ArgsManager &args, const fs::path &file_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: salvage.cpp:70
DatabaseStatus
Definition: db.h:205
const char * prefix
Definition: rest.cpp:1009
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:68
Bilingual messages:
Definition: translation.h:21
bool require_existing
Definition: db.h:192
bool verify
Check data integrity on load.
Definition: db.h:199
std::optional< DatabaseFormat > require_format
Definition: db.h:194
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:47
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1165
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:51