Bitcoin ABC 0.30.7
P2P Digital Currency
salvage.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 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 <config.h>
7#include <streams.h>
8#include <util/fs.h>
9#include <util/translation.h>
10#include <wallet/salvage.h>
11#include <wallet/wallet.h>
12#include <wallet/walletdb.h>
13
14/* End of headers, beginning of key/value data */
15static const char *HEADER_END = "HEADER=END";
16/* End of key/value data */
17static const char *DATA_END = "DATA=END";
18typedef std::pair<std::vector<uint8_t>, std::vector<uint8_t>> KeyValPair;
19
20static bool KeyFilter(const std::string &type) {
21 return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
22}
23
25 std::vector<bilingual_str> &warnings) {
26 DatabaseOptions options;
27 DatabaseStatus status;
28 options.require_existing = true;
29 options.verify = false;
30 std::unique_ptr<WalletDatabase> database =
31 MakeDatabase(file_path, options, status, error);
32 if (!database) {
33 return false;
34 }
35
36 std::string filename;
37 std::shared_ptr<BerkeleyEnvironment> env =
38 GetWalletEnv(file_path, filename);
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 // Set -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 error = strprintf(Untranslated("Failed to rename %s to %s"), filename,
58 newFilename);
59 return false;
60 }
61
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,
75 DB_SALVAGE | DB_AGGRESSIVE);
76 if (result == DB_VERIFY_BAD) {
77 warnings.push_back(
78 Untranslated("Salvage: Database salvage found errors, all data may "
79 "not be recoverable."));
80 }
81 if (result != 0 && result != DB_VERIFY_BAD) {
83 Untranslated("Salvage: Database salvage failed with result %d."),
84 result);
85 return false;
86 }
87
88 // Format of bdb dump is ascii lines:
89 // header lines...
90 // HEADER=END
91 // hexadecimal key
92 // hexadecimal value
93 // ... repeated
94 // DATA=END
95
96 std::string strLine;
97 while (!strDump.eof() && strLine != HEADER_END) {
98 getline(strDump, strLine); // Skip past header
99 }
100
101 std::string keyHex, valueHex;
102 while (!strDump.eof() && keyHex != DATA_END) {
103 getline(strDump, keyHex);
104 if (keyHex != DATA_END) {
105 if (strDump.eof()) {
106 break;
107 }
108 getline(strDump, valueHex);
109 if (valueHex == DATA_END) {
110 warnings.push_back(
111 Untranslated("Salvage: WARNING: Number of keys in data "
112 "does not match number of values."));
113 break;
114 }
115 salvagedData.push_back(
116 make_pair(ParseHex(keyHex), ParseHex(valueHex)));
117 }
118 }
119
120 bool fSuccess;
121 if (keyHex != DATA_END) {
122 warnings.push_back(Untranslated("Salvage: WARNING: Unexpected end of "
123 "file while reading salvage output."));
124 fSuccess = false;
125 } else {
126 fSuccess = (result == 0);
127 }
128
129 if (salvagedData.empty()) {
131 Untranslated("Salvage(aggressive) found no records in %s."),
132 newFilename);
133 return false;
134 }
135
136 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
137 int ret = pdbCopy->open(nullptr, // Txn pointer
138 filename.c_str(), // Filename
139 "main", // Logical db name
140 DB_BTREE, // Database type
141 DB_CREATE, // Flags
142 0);
143 if (ret > 0) {
144 error =
145 strprintf(Untranslated("Cannot create database file %s"), filename);
146 pdbCopy->close(0);
147 return false;
148 }
149
150 DbTxn *ptxn = env->TxnBegin();
151 CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
152 for (KeyValPair &row : salvagedData) {
153 /* Filter for only private key type KV pairs to be added to the salvaged
154 * wallet */
155 CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
156 CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
157 std::string strType, strErr;
158 bool fReadOK;
159 {
160 // Required in LoadKeyMetadata():
161 LOCK(dummyWallet.cs_wallet);
162 fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType,
163 strErr, KeyFilter);
164 }
165 if (!KeyFilter(strType)) {
166 continue;
167 }
168 if (!fReadOK) {
169 warnings.push_back(strprintf(
170 Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"),
171 strType, strErr));
172 continue;
173 }
174 Dbt datKey(row.first.data(), row.first.size());
175 Dbt datValue(row.second.data(), row.second.size());
176 int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
177 if (ret2 > 0) {
178 fSuccess = false;
179 }
180 }
181 ptxn->commit(0);
182 pdbCopy->close(0);
183
184 return fSuccess;
185}
std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
Definition: bdb.cpp:83
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:177
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:254
RecursiveMutex cs_wallet
Definition: wallet.h:389
static bool IsKeyType(const std::string &strType)
Definition: walletdb.cpp:769
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
bool error(const char *fmt, const Args &...args)
Definition: logging.h:263
const std::string HDCHAIN
Definition: walletdb.cpp:35
static const char * HEADER_END
Definition: salvage.cpp:15
static const char * DATA_END
Definition: salvage.cpp:17
std::pair< std::vector< uint8_t >, std::vector< uint8_t > > KeyValPair
Definition: salvage.cpp:18
bool RecoverDatabaseFile(const fs::path &file_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: salvage.cpp:24
static bool KeyFilter(const std::string &type)
Definition: salvage.cpp:20
CAddrDb db
Definition: main.cpp:35
@ SER_DISK
Definition: serialize.h:153
bool verify
Definition: db.h:226
bool require_existing
Definition: db.h:222
Bilingual messages:
Definition: translation.h:17
#define LOCK(cs)
Definition: sync.h:306
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:109
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
template std::vector< std::byte > ParseHex(std::string_view)
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1120
DatabaseStatus
Definition: db.h:229
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:303
std::unique_ptr< WalletDatabase > CreateDummyWalletDatabase()
Return object for accessing dummy database with no read/write capabilities.
Definition: walletdb.cpp:1170