Bitcoin Core  24.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 <fs.h>
7 #include <streams.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 namespace wallet {
15 /* End of headers, beginning of key/value data */
16 static const char *HEADER_END = "HEADER=END";
17 /* End of key/value data */
18 static const char *DATA_END = "DATA=END";
19 typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
20 
21 static bool KeyFilter(const std::string& type)
22 {
23  return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
24 }
25 
26 bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
27 {
28  DatabaseOptions options;
29  DatabaseStatus status;
30  ReadDatabaseArgs(args, options);
31  options.require_existing = true;
32  options.verify = false;
34  std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
35  if (!database) return false;
36 
37  BerkeleyDatabase& berkeley_database = static_cast<BerkeleyDatabase&>(*database);
38  std::string filename = berkeley_database.Filename();
39  std::shared_ptr<BerkeleyEnvironment> env = berkeley_database.env;
40 
41  if (!env->Open(error)) {
42  return false;
43  }
44 
45  // Recovery procedure:
46  // move wallet file to walletfilename.timestamp.bak
47  // Call Salvage with fAggressive=true to
48  // get as much data as possible.
49  // Rewrite salvaged data to fresh wallet file
50  // Rescan so any missing transactions will be
51  // found.
52  int64_t now = GetTime();
53  std::string newFilename = strprintf("%s.%d.bak", filename, now);
54 
55  int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
56  newFilename.c_str(), DB_AUTO_COMMIT);
57  if (result != 0)
58  {
59  error = strprintf(Untranslated("Failed to rename %s to %s"), filename, newFilename);
60  return false;
61  }
62 
69  std::vector<KeyValPair> salvagedData;
70 
71  std::stringstream strDump;
72 
73  Db db(env->dbenv.get(), 0);
74  result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
75  if (result == DB_VERIFY_BAD) {
76  warnings.push_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable."));
77  }
78  if (result != 0 && result != DB_VERIFY_BAD) {
79  error = strprintf(Untranslated("Salvage: Database salvage failed with result %d."), result);
80  return false;
81  }
82 
83  // Format of bdb dump is ascii lines:
84  // header lines...
85  // HEADER=END
86  // hexadecimal key
87  // hexadecimal value
88  // ... repeated
89  // DATA=END
90 
91  std::string strLine;
92  while (!strDump.eof() && strLine != HEADER_END)
93  getline(strDump, strLine); // Skip past header
94 
95  std::string keyHex, valueHex;
96  while (!strDump.eof() && keyHex != DATA_END) {
97  getline(strDump, keyHex);
98  if (keyHex != DATA_END) {
99  if (strDump.eof())
100  break;
101  getline(strDump, valueHex);
102  if (valueHex == DATA_END) {
103  warnings.push_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values."));
104  break;
105  }
106  salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
107  }
108  }
109 
110  bool fSuccess;
111  if (keyHex != DATA_END) {
112  warnings.push_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output."));
113  fSuccess = false;
114  } else {
115  fSuccess = (result == 0);
116  }
117 
118  if (salvagedData.empty())
119  {
120  error = strprintf(Untranslated("Salvage(aggressive) found no records in %s."), newFilename);
121  return false;
122  }
123 
124  std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
125  int ret = pdbCopy->open(nullptr, // Txn pointer
126  filename.c_str(), // Filename
127  "main", // Logical db name
128  DB_BTREE, // Database type
129  DB_CREATE, // Flags
130  0);
131  if (ret > 0) {
132  error = strprintf(Untranslated("Cannot create database file %s"), filename);
133  pdbCopy->close(0);
134  return false;
135  }
136 
137  DbTxn* ptxn = env->TxnBegin();
138  CWallet dummyWallet(nullptr, "", gArgs, CreateDummyWalletDatabase());
139  for (KeyValPair& row : salvagedData)
140  {
141  /* Filter for only private key type KV pairs to be added to the salvaged wallet */
142  DataStream ssKey{row.first};
143  CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
144  std::string strType, strErr;
145  bool fReadOK;
146  {
147  // Required in LoadKeyMetadata():
148  LOCK(dummyWallet.cs_wallet);
149  fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr, KeyFilter);
150  }
151  if (!KeyFilter(strType)) {
152  continue;
153  }
154  if (!fReadOK)
155  {
156  warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr));
157  continue;
158  }
159  Dbt datKey(row.first.data(), row.first.size());
160  Dbt datValue(row.second.data(), row.second.size());
161  int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
162  if (ret2 > 0)
163  fSuccess = false;
164  }
165  ptxn->commit(0);
166  pdbCopy->close(0);
167 
168  return fSuccess;
169 }
170 } // namespace wallet
int ret
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:186
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:31
An instance of this class represents one database.
Definition: bdb.h:95
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: bdb.h:156
std::string Filename() override
Return path to main database filename.
Definition: bdb.h:144
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:237
RecursiveMutex cs_wallet
Main wallet lock.
Definition: wallet.h:359
static bool IsKeyType(const std::string &strType)
Definition: walletdb.cpp:769
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:33
const std::string HDCHAIN
Definition: walletdb.cpp:41
Definition: node.h:39
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition: db.cpp:142
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1156
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Definition: salvage.cpp:19
static bool KeyFilter(const std::string &type)
Definition: salvage.cpp:21
static bool ReadKeyValue(CWallet *pwallet, DataStream &ssKey, CDataStream &ssValue, CWalletScanState &wss, std::string &strType, std::string &strErr, const KeyFilterFn &filter_fn=nullptr) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
Definition: walletdb.cpp:324
std::unique_ptr< WalletDatabase > CreateDummyWalletDatabase()
Return object for accessing dummy database with no read/write capabilities.
Definition: walletdb.cpp:1236
static const char * HEADER_END
Definition: salvage.cpp:16
static const char * DATA_END
Definition: salvage.cpp:18
bool RecoverDatabaseFile(const ArgsManager &args, const fs::path &file_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: salvage.cpp:26
DatabaseStatus
Definition: db.h:239
ArgsManager args
@ SER_DISK
Definition: serialize.h:132
Bilingual messages:
Definition: translation.h:18
bool require_existing
Definition: db.h:226
bool verify
Check data integrity on load.
Definition: db.h:233
std::optional< DatabaseFormat > require_format
Definition: db.h:228
#define LOCK(cs)
Definition: sync.h:258
bool error(const char *fmt, const Args &... args)
Definition: system.h:48
int64_t GetTime()
Definition: time.cpp:115
#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:48
std::vector< Byte > ParseHex(std::string_view str)
Parse the hex string into bytes (uint8_t or std::byte).
ArgsManager gArgs
Definition: system.cpp:73