Bitcoin Core 29.99.0
P2P Digital Currency
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 <util/fs.h>
9#include <util/translation.h>
10#include <wallet/wallet.h>
11#include <wallet/walletdb.h>
12
13#include <algorithm>
14#include <fstream>
15#include <memory>
16#include <string>
17#include <utility>
18#include <vector>
19
20namespace wallet {
21static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
22uint32_t DUMP_VERSION = 1;
23
25{
26 // Get the dumpfile
27 std::string dump_filename = args.GetArg("-dumpfile", "");
28 if (dump_filename.empty()) {
29 error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
30 return false;
31 }
32
33 fs::path path = fs::PathFromString(dump_filename);
34 path = fs::absolute(path);
35 if (fs::exists(path)) {
36 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));
37 return false;
38 }
39 std::ofstream dump_file;
40 dump_file.open(path);
41 if (dump_file.fail()) {
42 error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
43 return false;
44 }
45
46 HashWriter hasher{};
47
48 std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
49
50 bool ret = true;
51 std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
52 if (!cursor) {
53 error = _("Error: Couldn't create cursor into database");
54 ret = false;
55 }
56
57 // Write out a magic string with version
58 std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
59 dump_file.write(line.data(), line.size());
60 hasher << std::span{line};
61
62 // Write out the file format
63 std::string format = db.Format();
64 // BDB files that are opened using BerkeleyRODatabase have it's format as "bdb_ro"
65 // We want to override that format back to "bdb"
66 if (format == "bdb_ro") {
67 format = "bdb";
68 }
69 line = strprintf("%s,%s\n", "format", format);
70 dump_file.write(line.data(), line.size());
71 hasher << std::span{line};
72
73 if (ret) {
74
75 // Read the records
76 while (true) {
77 DataStream ss_key{};
78 DataStream ss_value{};
79 DatabaseCursor::Status status = cursor->Next(ss_key, ss_value);
80 if (status == DatabaseCursor::Status::DONE) {
81 ret = true;
82 break;
83 } else if (status == DatabaseCursor::Status::FAIL) {
84 error = _("Error reading next record from wallet database");
85 ret = false;
86 break;
87 }
88 std::string key_str = HexStr(ss_key);
89 std::string value_str = HexStr(ss_value);
90 line = strprintf("%s,%s\n", key_str, value_str);
91 dump_file.write(line.data(), line.size());
92 hasher << std::span{line};
93 }
94 }
95
96 cursor.reset();
97 batch.reset();
98
99 if (ret) {
100 // Write the hash
101 tfm::format(dump_file, "checksum,%s\n", HexStr(hasher.GetHash()));
102 dump_file.close();
103 } else {
104 // Remove the dumpfile on failure
105 dump_file.close();
106 fs::remove(path);
107 }
108
109 return ret;
110}
111
112// The standard wallet deleter function blocks on the validation interface
113// queue, which doesn't exist for the bitcoin-wallet. Define our own
114// deleter here.
116{
117 wallet->WalletLogPrintf("Releasing wallet\n");
118 wallet->Close();
119 delete wallet;
120}
121
122bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
123{
124 // Get the dumpfile
125 std::string dump_filename = args.GetArg("-dumpfile", "");
126 if (dump_filename.empty()) {
127 error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
128 return false;
129 }
130
131 fs::path dump_path = fs::PathFromString(dump_filename);
132 dump_path = fs::absolute(dump_path);
133 if (!fs::exists(dump_path)) {
134 error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
135 return false;
136 }
137 std::ifstream dump_file{dump_path};
138
139 // Compute the checksum
140 HashWriter hasher{};
141 uint256 checksum;
142
143 // Check the magic and version
144 std::string magic_key;
145 std::getline(dump_file, magic_key, ',');
146 std::string version_value;
147 std::getline(dump_file, version_value, '\n');
148 if (magic_key != DUMP_MAGIC) {
149 error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC);
150 dump_file.close();
151 return false;
152 }
153 // Check the version number (value of first record)
154 uint32_t ver;
155 if (!ParseUInt32(version_value, &ver)) {
156 error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
157 dump_file.close();
158 return false;
159 }
160 if (ver != DUMP_VERSION) {
161 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);
162 dump_file.close();
163 return false;
164 }
165 std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
166 hasher << std::span{magic_hasher_line};
167
168 // Get the stored file format
169 std::string format_key;
170 std::getline(dump_file, format_key, ',');
171 std::string format_value;
172 std::getline(dump_file, format_value, '\n');
173 if (format_key != "format") {
174 error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
175 dump_file.close();
176 return false;
177 }
178 // Make sure that the dump was created from a sqlite database only as that is the only
179 // type of database that we still support.
180 // Other formats such as BDB should not be loaded into a sqlite database since they also
181 // use a different type of wallet entirely which is no longer compatible with this software.
182 if (format_value != "sqlite") {
183 error = strprintf(_("Error: Dumpfile specifies an unsupported database format (%s). Only sqlite database dumps are supported"), format_value);
184 return false;
185 }
186 std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
187 hasher << std::span{format_hasher_line};
188
189 DatabaseOptions options;
190 DatabaseStatus status;
191 ReadDatabaseArgs(args, options);
192 options.require_create = true;
194 std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
195 if (!database) return false;
196
197 // dummy chain interface
198 bool ret = true;
199 std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet);
200 {
201 LOCK(wallet->cs_wallet);
202 DBErrors load_wallet_ret = wallet->LoadWallet();
203 if (load_wallet_ret != DBErrors::LOAD_OK) {
204 error = strprintf(_("Error creating %s"), name);
205 return false;
206 }
207
208 // Get the database handle
209 WalletDatabase& db = wallet->GetDatabase();
210 std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
211 batch->TxnBegin();
212
213 // Read the records from the dump file and write them to the database
214 while (dump_file.good()) {
215 std::string key;
216 std::getline(dump_file, key, ',');
217 std::string value;
218 std::getline(dump_file, value, '\n');
219
220 if (key == "checksum") {
221 std::vector<unsigned char> parsed_checksum = ParseHex(value);
222 if (parsed_checksum.size() != checksum.size()) {
223 error = Untranslated("Error: Checksum is not the correct size");
224 ret = false;
225 break;
226 }
227 std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
228 break;
229 }
230
231 std::string line = strprintf("%s,%s\n", key, value);
232 hasher << std::span{line};
233
234 if (key.empty() || value.empty()) {
235 continue;
236 }
237
238 if (!IsHex(key)) {
239 error = strprintf(_("Error: Got key that was not hex: %s"), key);
240 ret = false;
241 break;
242 }
243 if (!IsHex(value)) {
244 error = strprintf(_("Error: Got value that was not hex: %s"), value);
245 ret = false;
246 break;
247 }
248
249 std::vector<unsigned char> k = ParseHex(key);
250 std::vector<unsigned char> v = ParseHex(value);
251 if (!batch->Write(std::span{k}, std::span{v})) {
252 error = strprintf(_("Error: Unable to write record to new wallet"));
253 ret = false;
254 break;
255 }
256 }
257
258 if (ret) {
259 uint256 comp_checksum = hasher.GetHash();
260 if (checksum.IsNull()) {
261 error = _("Error: Missing checksum");
262 ret = false;
263 } else if (checksum != comp_checksum) {
264 error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
265 ret = false;
266 }
267 }
268
269 if (ret) {
270 batch->TxnCommit();
271 } else {
272 batch->TxnAbort();
273 }
274
275 batch.reset();
276
277 dump_file.close();
278 }
279 wallet.reset(); // The pointer deleter will close the wallet for us.
280
281 // Remove the wallet dir if we have a failure
282 if (!ret) {
283 fs::remove_all(wallet_path);
284 }
285
286 return ret;
287}
288} // namespace wallet
int ret
ArgsManager & args
Definition: bitcoind.cpp:277
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:457
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:101
constexpr bool IsNull() const
Definition: uint256.h:48
static constexpr unsigned int size()
Definition: uint256.h:107
constexpr unsigned char * begin()
Definition: uint256.h:101
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:300
An instance of this class represents one database.
Definition: db.h:130
virtual std::string Format()=0
virtual std::unique_ptr< DatabaseBatch > MakeBatch()=0
Make a DatabaseBatch connected to this database.
static path absolute(const path &p)
Definition: fs.h:82
static bool exists(const path &p)
Definition: fs.h:89
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:151
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:174
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:29
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1079
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition: db.cpp:154
static void WalletToolReleaseWallet(CWallet *wallet)
Definition: dump.cpp:115
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1342
uint32_t DUMP_VERSION
Definition: dump.cpp:22
DBErrors
Overview of wallet database classes:
Definition: walletdb.h:40
bool CreateFromDump(const ArgsManager &args, const std::string &name, const fs::path &wallet_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: dump.cpp:122
bool DumpWallet(const ArgsManager &args, WalletDatabase &db, bilingual_str &error)
Definition: dump.cpp:24
static const std::string DUMP_MAGIC
Definition: dump.cpp:21
DatabaseStatus
Definition: db.h:183
const char * name
Definition: rest.cpp:49
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:68
Bilingual messages:
Definition: translation.h:24
std::optional< DatabaseFormat > require_format
Definition: db.h:172
#define LOCK(cs)
Definition: sync.h:257
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
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)
bool ParseUInt32(std::string_view str, uint32_t *out)
Convert decimal string to unsigned 32-bit integer with strict parse error feedback.