Bitcoin Core  29.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  bool HasActiveTxn() override { return false; }
48 };
49 
53 {
54 public:
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; }
63  void IncrementUpdateCounter() override { ++nUpdateCounter; }
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 
70 bool 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
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1409
const std::string MASTER_KEY
Definition: walletdb.cpp:50
std::optional< DatabaseFormat > require_format
Definition: db.h:194
bool HasKey(DataStream &&key) override
Definition: salvage.cpp:35
const std::string CRYPTED_KEY
Definition: walletdb.cpp:41
int ret
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition: db.cpp:154
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
Definition: salvage.cpp:33
const std::string HDCHAIN
Definition: walletdb.cpp:46
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:68
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Definition: salvage.cpp:59
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Definition: salvage.cpp:21
Bilingual messages:
Definition: translation.h:24
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:1172
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:70
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:82
const char * prefix
Definition: rest.cpp:1009
bool HasActiveTxn() override
Definition: salvage.cpp:47
void Close() override
Flush to the database file and close the database.
Definition: salvage.cpp:60
bool LoadHDChain(CWallet *pwallet, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:453
bool ErasePrefix(Span< const std::byte > prefix) override
Definition: salvage.cpp:36
const std::string KEY
Definition: walletdb.cpp:48
std::string Filename() override
Return path to main database file for logs and error messages.
Definition: salvage.cpp:65
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:175
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
Definition: salvage.cpp:67
RAII class that provides access to a WalletDatabase.
Definition: db.h:50
bool TxnCommit() override
Definition: salvage.cpp:45
static const char * HEADER_END
Definition: salvage.cpp:18
bool PeriodicFlush() override
Definition: salvage.cpp:62
bool LoadEncryptionKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:426
ArgsManager & args
Definition: bitcoind.cpp:277
bool LoadCryptedKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:387
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:146
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:61
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:299
void ReloadDbEnv() override
Definition: salvage.cpp:64
void IncrementUpdateCounter() override
Definition: salvage.cpp:63
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:56
bool EraseKey(DataStream &&key) override
Definition: salvage.cpp:34
DatabaseStatus
Definition: db.h:205
A dummy WalletDatabase that does nothing and never fails.
Definition: salvage.cpp:52
bool LoadKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
Definition: walletdb.cpp:321
auto result
Definition: common-types.h:74
static const char * DATA_END
Definition: salvage.cpp:20
std::unique_ptr< DatabaseCursor > GetNewCursor() override
Definition: salvage.cpp:42
bool require_existing
Definition: db.h:192
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:58
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
int64_t GetTime()
DEPRECATED, see GetTime.
Definition: time.cpp:76
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:32
bool verify
Check data integrity on load.
Definition: db.h:199
An instance of this class represents one database.
Definition: db.h:130
void Open() override
Open the database if it is not already opened.
Definition: salvage.cpp:55