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