Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
dump.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <wallet/dump.h>
6
7#include <common/args.h>
8#include <streams.h>
9#include <util/fs.h>
10#include <util/fs_helpers.h>
11#include <util/translation.h>
12#include <wallet/wallet.h>
13#include <wallet/walletdb.h>
14
15#include <algorithm>
16#include <fstream>
17#include <memory>
18#include <string>
19#include <utility>
20#include <vector>
21
22namespace wallet {
23static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
25
27{
28 // Get the dumpfile
29 std::string dump_filename = args.GetArg("-dumpfile", "");
30 if (dump_filename.empty()) {
31 error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
32 return false;
33 }
34
35 fs::path path = fs::PathFromString(dump_filename);
36 path = fs::absolute(path);
37 if (fs::exists(path)) {
38 error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path));
39 return false;
40 }
42 if (dump_file.IsNull()) {
43 error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
44 return false;
45 }
46
48
49 std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
50
51 struct DumpWalletError {
52 bilingual_str message;
53 };
54 auto throw_error = [](bilingual_str message) {
55 throw DumpWalletError{std::move(message)};
56 };
57 auto throw_write_error = [&] {
58 throw_error(strprintf(_("Unable to write complete dump file %s."), fs::PathToString(path)));
59 };
60 auto write = [&](const std::string& line) {
61 try {
62 dump_file.write(MakeByteSpan(line));
63 } catch (const std::ios_base::failure&) {
65 }
66 };
67 auto write_record = [&](const std::string& line) {
68 write(line);
69 hasher << std::span{line};
70 };
71
72 try {
73 std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
74 if (!cursor) {
75 throw_error(_("Error: Couldn't create cursor into database"));
76 }
77
78 // Write out a magic string with version
79 std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
80 write_record(line);
81
82 // Write out the file format
83 std::string format = db.Format();
84 // BDB files that are opened using BerkeleyRODatabase have its format as "bdb_ro"
85 // We want to override that format back to "bdb"
86 if (format == "bdb_ro") {
87 format = "bdb";
88 }
89 line = strprintf("%s,%s\n", "format", format);
90 write_record(line);
91
92 // Read the records
93 while (true) {
97 if (status == DatabaseCursor::Status::DONE) {
98 break;
99 } else if (status == DatabaseCursor::Status::FAIL) {
100 throw_error(_("Error reading next record from wallet database"));
101 }
102 std::string key_str = HexStr(ss_key);
103 std::string value_str = HexStr(ss_value);
104 line = strprintf("%s,%s\n", key_str, value_str);
105 write_record(line);
106 }
107
108 // Write the hash
109 line = strprintf("checksum,%s\n", HexStr(hasher.GetHash()));
110 write(line);
111 if (!dump_file.Commit()) throw_write_error();
112 if (dump_file.fclose() != 0) throw_write_error();
113 DirectoryCommit(path.parent_path());
114 } catch (const DumpWalletError& e) {
115 error = e.message;
116 // Remove the dumpfile on failure
117 (void)dump_file.fclose();
118 fs::remove(path);
119 return false;
120 }
121
122 return true;
123}
124
125// The standard wallet deleter function blocks on the validation interface
126// queue, which doesn't exist for the bitcoin-wallet. Define our own
127// deleter here.
129{
130 wallet->WalletLogPrintf("Releasing wallet");
131 wallet->Close();
132 delete wallet;
133}
134
135bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error)
136{
137 if (name.empty()) {
138 tfm::format(std::cerr, "Wallet name cannot be empty\n");
139 return false;
140 }
141
142 // Get the dumpfile
143 std::string dump_filename = args.GetArg("-dumpfile", "");
144 if (dump_filename.empty()) {
145 error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
146 return false;
147 }
148
151 if (!fs::exists(dump_path)) {
152 error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
153 return false;
154 }
155 std::ifstream dump_file{dump_path.std_path()};
156
157 // Compute the checksum
160
161 // Check the magic and version
162 std::string magic_key;
163 std::getline(dump_file, magic_key, ',');
164 std::string version_value;
165 std::getline(dump_file, version_value, '\n');
166 if (magic_key != DUMP_MAGIC) {
167 error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC);
168 dump_file.close();
169 return false;
170 }
171 // Check the version number (value of first record)
173 if (!ver) {
174 error = strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
175 dump_file.close();
176 return false;
177 }
178 if (*ver != DUMP_VERSION) {
179 error = strprintf(_("Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value);
180 dump_file.close();
181 return false;
182 }
183 std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
184 hasher << std::span{magic_hasher_line};
185
186 // Get the stored file format
187 std::string format_key;
188 std::getline(dump_file, format_key, ',');
189 std::string format_value;
190 std::getline(dump_file, format_value, '\n');
191 if (format_key != "format") {
192 error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
193 dump_file.close();
194 return false;
195 }
196 // Make sure that the dump was created from a sqlite database only as that is the only
197 // type of database that we still support.
198 // Other formats such as BDB should not be loaded into a sqlite database since they also
199 // use a different type of wallet entirely which is no longer compatible with this software.
200 if (format_value != "sqlite") {
201 error = strprintf(_("Error: Dumpfile specifies an unsupported database format (%s). Only sqlite database dumps are supported"), format_value);
202 return false;
203 }
204 std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
205 hasher << std::span{format_hasher_line};
206
207 DatabaseOptions options;
208 DatabaseStatus status;
209 ReadDatabaseArgs(args, options);
210 options.require_create = true;
212 std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
213 if (!database) return false;
214
215 // dummy chain interface
216 bool ret = true;
217 std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet);
218 {
219 // Get the database handle
220 WalletDatabase& db = wallet->GetDatabase();
221 std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
222 batch->TxnBegin();
223
224 // Read the records from the dump file and write them to the database
225 while (dump_file.good()) {
226 std::string key;
227 std::getline(dump_file, key, ',');
228 std::string value;
229 std::getline(dump_file, value, '\n');
230
231 if (key == "checksum") {
232 std::vector<unsigned char> parsed_checksum = ParseHex(value);
233 if (parsed_checksum.size() != checksum.size()) {
234 error = Untranslated("Error: Checksum is not the correct size");
235 ret = false;
236 break;
237 }
238 std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
239 break;
240 }
241
242 std::string line = strprintf("%s,%s\n", key, value);
243 hasher << std::span{line};
244
245 if (key.empty() || value.empty()) {
246 continue;
247 }
248
249 if (!IsHex(key)) {
250 error = strprintf(_("Error: Got key that was not hex: %s"), key);
251 ret = false;
252 break;
253 }
254 if (!IsHex(value)) {
255 error = strprintf(_("Error: Got value that was not hex: %s"), value);
256 ret = false;
257 break;
258 }
259
260 std::vector<unsigned char> k = ParseHex(key);
261 std::vector<unsigned char> v = ParseHex(value);
262 if (!batch->Write(std::span{k}, std::span{v})) {
263 error = strprintf(_("Error: Unable to write record to new wallet"));
264 ret = false;
265 break;
266 }
267 }
268
269 if (ret) {
270 uint256 comp_checksum = hasher.GetHash();
271 if (checksum.IsNull()) {
272 error = _("Error: Missing checksum");
273 ret = false;
274 } else if (checksum != comp_checksum) {
275 error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
276 ret = false;
277 }
278 }
279
280 if (ret) {
281 batch->TxnCommit();
282 } else {
283 batch->TxnAbort();
284 }
285
286 batch.reset();
287
288 dump_file.close();
289 }
290 // On failure, gather the paths to remove
291 std::vector<fs::path> paths_to_remove = wallet->GetDatabase().Files();
292 if (!name.empty()) paths_to_remove.push_back(wallet_path);
293
294 wallet.reset(); // The pointer deleter will close the wallet for us.
295
296 // Remove the wallet dir if we have a failure
297 if (!ret) {
298 for (const auto& p : paths_to_remove) {
299 fs::remove(p);
300 }
301 }
302
303 return ret;
304}
305} // namespace wallet
int ret
ArgsManager & args
Definition bitcoind.cpp:280
std::string GetArg(const std::string &strArg, const std::string &strDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return string argument or default value.
Definition args.cpp:518
Non-refcounted RAII wrapper for FILE*.
Definition streams.h:395
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:165
A writer stream (for serialization) that computes a 256-bit hash.
Definition hash.h:109
256-bit opaque blob.
Definition uint256.h:196
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition wallet.h:318
An instance of this class represents one database.
Definition db.h:129
static path absolute(const path &p)
Definition fs.h:87
static bool exists(const path &p)
Definition fs.h:94
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition fs.h:160
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition fs.h:183
void DirectoryCommit(const fs::path &dirname)
Sync directory contents.
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition hex_base.cpp:30
FILE * fopen(const fs::path &p, const char *mode)
Definition fs.cpp:23
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition db.cpp:153
static void WalletToolReleaseWallet(CWallet *wallet)
Definition dump.cpp:128
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
uint32_t DUMP_VERSION
Definition dump.cpp:24
bool CreateFromDump(const ArgsManager &args, const std::string &name, const fs::path &wallet_path, bilingual_str &error)
Definition dump.cpp:135
bool DumpWallet(const ArgsManager &args, WalletDatabase &db, bilingual_str &error)
Definition dump.cpp:26
static const std::string DUMP_MAGIC
Definition dump.cpp:23
DatabaseStatus
Definition db.h:180
const char * name
Definition rest.cpp:71
auto MakeByteSpan(const V &v) noexcept
Definition span.h:84
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Bilingual messages:
Definition translation.h:24
std::optional< DatabaseFormat > require_format
Definition db.h:171
CDBWrapper db
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
consteval auto _(util::TranslatedLiteral str)
Definition translation.h:79
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition translation.h:82
bool IsHex(std::string_view str)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82