Bitcoin Core  27.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 
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
int ret
ArgsManager & args
Definition: bitcoind.cpp:267
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:301
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
std::unique_ptr< DatabaseCursor > GetNewCursor() override
Definition: salvage.cpp:42
bool TxnAbort() override
Definition: salvage.cpp:46
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
bool TxnCommit() override
Definition: salvage.cpp:45
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
Definition: salvage.cpp:43
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::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
Definition: salvage.cpp:66
std::string Filename() override
Return path to main database file for logs and error messages.
Definition: salvage.cpp:64
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:145
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1349
bool LoadKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:305
bool LoadCryptedKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:371
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:410
bool LoadHDChain(CWallet *pwallet, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:437
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:1005
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:65
Bilingual messages:
Definition: translation.h:18
bool require_existing
Definition: db.h:191
bool verify
Check data integrity on load.
Definition: db.h:198
std::optional< DatabaseFormat > require_format
Definition: db.h:193
int64_t GetTime()
Definition: time.cpp:44
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1162
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:48