Bitcoin Core  26.1.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 
16 namespace wallet {
17 /* End of headers, beginning of key/value data */
18 static const char *HEADER_END = "HEADER=END";
19 /* End of key/value data */
20 static const char *DATA_END = "DATA=END";
21 typedef 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 
29 class DummyBatch : public DatabaseBatch
30 {
31 private:
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 
38 public:
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 {
53 public:
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; }
62  void IncrementUpdateCounter() override { ++nUpdateCounter; }
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 
69 bool 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
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1437
const std::string MASTER_KEY
Definition: walletdb.cpp:47
std::optional< DatabaseFormat > require_format
Definition: db.h:186
bool HasKey(DataStream &&key) override
Definition: salvage.cpp:35
const std::string CRYPTED_KEY
Definition: walletdb.cpp:38
int ret
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition: db.cpp:142
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
Definition: salvage.cpp:33
const std::string HDCHAIN
Definition: walletdb.cpp:43
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:65
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Definition: salvage.cpp:58
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Definition: salvage.cpp:21
Bilingual messages:
Definition: translation.h:18
bool ReadKey(DataStream &&key, DataStream &value) override
Definition: salvage.cpp:32
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1162
Status Next(DataStream &key, DataStream &value) override
Definition: salvage.cpp:25
bool RecoverDatabaseFile(const ArgsManager &args, const fs::path &file_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: salvage.cpp:69
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:48
const char * prefix
Definition: rest.cpp:1004
void Close() override
Flush to the database file and close the database.
Definition: salvage.cpp:59
bool LoadHDChain(CWallet *pwallet, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:434
bool ErasePrefix(Span< const std::byte > prefix) override
Definition: salvage.cpp:36
const std::string KEY
Definition: walletdb.cpp:45
std::string Filename() override
Return path to main database file for logs and error messages.
Definition: salvage.cpp:64
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:169
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
Definition: salvage.cpp:66
RAII class that provides access to a WalletDatabase.
Definition: db.h:45
bool TxnCommit() override
Definition: salvage.cpp:45
static const char * HEADER_END
Definition: salvage.cpp:18
bool PeriodicFlush() override
Definition: salvage.cpp:61
bool LoadEncryptionKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:407
ArgsManager & args
Definition: bitcoind.cpp:269
bool LoadCryptedKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:368
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:192
bool TxnAbort() override
Definition: salvage.cpp:46
RAII class that provides access to a DummyDatabase.
Definition: salvage.cpp:29
void Flush() override
Make sure all changes are flushed to database file.
Definition: salvage.cpp:60
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:300
void ReloadDbEnv() override
Definition: salvage.cpp:63
void IncrementUpdateCounter() override
Definition: salvage.cpp:62
void Flush() override
Definition: salvage.cpp:39
An instance of this class represents one database.
Definition: bdb.h:88
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
Definition: salvage.cpp:43
void AddRef() override
Indicate the a new database user has began using the database.
Definition: salvage.cpp:55
bool EraseKey(DataStream &&key) override
Definition: salvage.cpp:34
DatabaseStatus
Definition: db.h:197
A dummy WalletDatabase that does nothing and never fails.
Definition: salvage.cpp:51
bool LoadKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:302
static const char * DATA_END
Definition: salvage.cpp:20
bool error(const char *fmt, const Args &... args)
Definition: logging.h:262
std::unique_ptr< DatabaseCursor > GetNewCursor() override
Definition: salvage.cpp:42
bool require_existing
Definition: db.h:184
bool TxnBegin() override
Definition: salvage.cpp:44
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
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
int64_t GetTime()
DEPRECATED, see GetTime.
Definition: time.cpp:97
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: bdb.h:145
void Close() override
Definition: salvage.cpp:40
std::string Filename() override
Return path to main database filename.
Definition: bdb.h:133
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
bool verify
Check data integrity on load.
Definition: db.h:191
An instance of this class represents one database.
Definition: db.h:124
void Open() override
Open the database if it is not already opened.
Definition: salvage.cpp:54