Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
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};
48
52{
53public:
54 void Open() override {};
55 void AddRef() override {}
56 void RemoveRef() override {}
57 bool Rewrite(const char* pszSkip=nullptr) override { return true; }
58 bool Backup(const std::string& strDest) const override { return true; }
59 void Close() override {}
60 void Flush() override {}
61 bool PeriodicFlush() override { return true; }
63 void ReloadDbEnv() override {}
64 std::string Filename() override { return "dummy"; }
65 std::string Format() override { return "dummy"; }
66 std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
67};
68
69bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
70{
71 DatabaseOptions options;
72 DatabaseStatus status;
73 ReadDatabaseArgs(args, options);
74 options.require_existing = true;
75 options.verify = false;
77 std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
78 if (!database) return false;
79
80 BerkeleyDatabase& berkeley_database = static_cast<BerkeleyDatabase&>(*database);
81 std::string filename = berkeley_database.Filename();
82 std::shared_ptr<BerkeleyEnvironment> env = berkeley_database.env;
83
84 if (!env->Open(error)) {
85 return false;
86 }
87
88 // Recovery procedure:
89 // move wallet file to walletfilename.timestamp.bak
90 // Call Salvage with fAggressive=true to
91 // get as much data as possible.
92 // Rewrite salvaged data to fresh wallet file
93 // Rescan so any missing transactions will be
94 // found.
95 int64_t now = GetTime();
96 std::string newFilename = strprintf("%s.%d.bak", filename, now);
97
98 int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
99 newFilename.c_str(), DB_AUTO_COMMIT);
100 if (result != 0)
101 {
102 error = strprintf(Untranslated("Failed to rename %s to %s"), filename, newFilename);
103 return false;
104 }
105
112 std::vector<KeyValPair> salvagedData;
113
114 std::stringstream strDump;
115
116 Db db(env->dbenv.get(), 0);
117 result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
118 if (result == DB_VERIFY_BAD) {
119 warnings.push_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable."));
120 }
121 if (result != 0 && result != DB_VERIFY_BAD) {
122 error = strprintf(Untranslated("Salvage: Database salvage failed with result %d."), result);
123 return false;
124 }
125
126 // Format of bdb dump is ascii lines:
127 // header lines...
128 // HEADER=END
129 // hexadecimal key
130 // hexadecimal value
131 // ... repeated
132 // DATA=END
133
134 std::string strLine;
135 while (!strDump.eof() && strLine != HEADER_END)
136 getline(strDump, strLine); // Skip past header
137
138 std::string keyHex, valueHex;
139 while (!strDump.eof() && keyHex != DATA_END) {
140 getline(strDump, keyHex);
141 if (keyHex != DATA_END) {
142 if (strDump.eof())
143 break;
144 getline(strDump, valueHex);
145 if (valueHex == DATA_END) {
146 warnings.push_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values."));
147 break;
148 }
149 salvagedData.emplace_back(ParseHex(keyHex), ParseHex(valueHex));
150 }
151 }
152
153 bool fSuccess;
154 if (keyHex != DATA_END) {
155 warnings.push_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output."));
156 fSuccess = false;
157 } else {
158 fSuccess = (result == 0);
159 }
160
161 if (salvagedData.empty())
162 {
163 error = strprintf(Untranslated("Salvage(aggressive) found no records in %s."), newFilename);
164 return false;
165 }
166
167 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
168 int ret = pdbCopy->open(nullptr, // Txn pointer
169 filename.c_str(), // Filename
170 "main", // Logical db name
171 DB_BTREE, // Database type
172 DB_CREATE, // Flags
173 0);
174 if (ret > 0) {
175 error = strprintf(Untranslated("Cannot create database file %s"), filename);
176 pdbCopy->close(0);
177 return false;
178 }
179
180 DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
181 CWallet dummyWallet(nullptr, "", std::make_unique<DummyDatabase>());
182 for (KeyValPair& row : salvagedData)
183 {
184 /* Filter for only private key type KV pairs to be added to the salvaged wallet */
185 DataStream ssKey{row.first};
186 DataStream ssValue(row.second);
187 std::string strType, strErr;
188
189 // We only care about KEY, MASTER_KEY, CRYPTED_KEY, and HDCHAIN types
190 ssKey >> strType;
191 bool fReadOK = false;
192 if (strType == DBKeys::KEY) {
193 fReadOK = LoadKey(&dummyWallet, ssKey, ssValue, strErr);
194 } else if (strType == DBKeys::CRYPTED_KEY) {
195 fReadOK = LoadCryptedKey(&dummyWallet, ssKey, ssValue, strErr);
196 } else if (strType == DBKeys::MASTER_KEY) {
197 fReadOK = LoadEncryptionKey(&dummyWallet, ssKey, ssValue, strErr);
198 } else if (strType == DBKeys::HDCHAIN) {
199 fReadOK = LoadHDChain(&dummyWallet, ssValue, strErr);
200 } else {
201 continue;
202 }
203
204 if (!fReadOK)
205 {
206 warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr));
207 continue;
208 }
209 Dbt datKey(row.first.data(), row.first.size());
210 Dbt datValue(row.second.data(), row.second.size());
211 int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
212 if (ret2 > 0)
213 fSuccess = false;
214 }
215 ptxn->commit(0);
216 pdbCopy->close(0);
217
218 return fSuccess;
219}
220} // namespace wallet
int ret
ArgsManager & args
Definition bitcoind.cpp:270
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:147
A Span is an object that can refer to a contiguous sequence of objects.
Definition span.h:98
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 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:52
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:57
void AddRef() override
Indicate the a new database user has began using the database.
Definition salvage.cpp:55
void ReloadDbEnv() override
Definition salvage.cpp:63
bool PeriodicFlush() override
Definition salvage.cpp:61
void IncrementUpdateCounter() override
Definition salvage.cpp:62
void Flush() override
Make sure all changes are flushed to database file.
Definition salvage.cpp:60
std::string Filename() override
Return path to main database file for logs and error messages.
Definition salvage.cpp:64
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
Definition salvage.cpp:66
void Open() override
Open the database if it is not already opened.
Definition salvage.cpp:54
std::string Format() override
Definition salvage.cpp:65
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
Definition salvage.cpp:56
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Definition salvage.cpp:58
void Close() override
Flush to the database file and close the database.
Definition salvage.cpp:59
An instance of this class represents one database.
Definition db.h:130
std::atomic< unsigned int > nUpdateCounter
Definition db.h:174
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:153
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
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:69
DatabaseStatus
Definition db.h:204
const char * prefix
Definition rest.cpp:1007
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Bilingual messages:
Definition translation.h:18
bool verify
Check data integrity on load.
Definition db.h:198
std::optional< DatabaseFormat > require_format
Definition db.h:193
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition time.cpp:44
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition translation.h:48