Bitcoin Core 22.99.0
P2P Digital Currency
dump.cpp
Go to the documentation of this file.
1// Copyright (c) 2020 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 <util/translation.h>
8#include <wallet/wallet.h>
9
10static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
11uint32_t DUMP_VERSION = 1;
12
14{
15 // Get the dumpfile
16 std::string dump_filename = gArgs.GetArg("-dumpfile", "");
17 if (dump_filename.empty()) {
18 error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
19 return false;
20 }
21
22 fs::path path = fs::PathFromString(dump_filename);
23 path = fs::absolute(path);
24 if (fs::exists(path)) {
25 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));
26 return false;
27 }
28 fsbridge::ofstream dump_file;
29 dump_file.open(path);
30 if (dump_file.fail()) {
31 error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
32 return false;
33 }
34
35 CHashWriter hasher(0, 0);
36
37 WalletDatabase& db = wallet.GetDatabase();
38 std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
39
40 bool ret = true;
41 if (!batch->StartCursor()) {
42 error = _("Error: Couldn't create cursor into database");
43 ret = false;
44 }
45
46 // Write out a magic string with version
47 std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
48 dump_file.write(line.data(), line.size());
49 hasher.write(line.data(), line.size());
50
51 // Write out the file format
52 line = strprintf("%s,%s\n", "format", db.Format());
53 dump_file.write(line.data(), line.size());
54 hasher.write(line.data(), line.size());
55
56 if (ret) {
57
58 // Read the records
59 while (true) {
62 bool complete;
63 ret = batch->ReadAtCursor(ss_key, ss_value, complete);
64 if (complete) {
65 ret = true;
66 break;
67 } else if (!ret) {
68 error = _("Error reading next record from wallet database");
69 break;
70 }
71 std::string key_str = HexStr(ss_key);
72 std::string value_str = HexStr(ss_value);
73 line = strprintf("%s,%s\n", key_str, value_str);
74 dump_file.write(line.data(), line.size());
75 hasher.write(line.data(), line.size());
76 }
77 }
78
79 batch->CloseCursor();
80 batch.reset();
81
82 // Close the wallet after we're done with it. The caller won't be doing this
83 wallet.Close();
84
85 if (ret) {
86 // Write the hash
87 tfm::format(dump_file, "checksum,%s\n", HexStr(hasher.GetHash()));
88 dump_file.close();
89 } else {
90 // Remove the dumpfile on failure
91 dump_file.close();
92 fs::remove(path);
93 }
94
95 return ret;
96}
97
98// The standard wallet deleter function blocks on the validation interface
99// queue, which doesn't exist for the bitcoin-wallet. Define our own
100// deleter here.
102{
103 wallet->WalletLogPrintf("Releasing wallet\n");
104 wallet->Close();
105 delete wallet;
106}
107
108bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
109{
110 // Get the dumpfile
111 std::string dump_filename = gArgs.GetArg("-dumpfile", "");
112 if (dump_filename.empty()) {
113 error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
114 return false;
115 }
116
117 fs::path dump_path = fs::PathFromString(dump_filename);
118 dump_path = fs::absolute(dump_path);
119 if (!fs::exists(dump_path)) {
120 error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
121 return false;
122 }
123 fsbridge::ifstream dump_file(dump_path);
124
125 // Compute the checksum
126 CHashWriter hasher(0, 0);
127 uint256 checksum;
128
129 // Check the magic and version
130 std::string magic_key;
131 std::getline(dump_file, magic_key, ',');
132 std::string version_value;
133 std::getline(dump_file, version_value, '\n');
134 if (magic_key != DUMP_MAGIC) {
135 error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC);
136 dump_file.close();
137 return false;
138 }
139 // Check the version number (value of first record)
140 uint32_t ver;
141 if (!ParseUInt32(version_value, &ver)) {
142 error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
143 dump_file.close();
144 return false;
145 }
146 if (ver != DUMP_VERSION) {
147 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);
148 dump_file.close();
149 return false;
150 }
151 std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
152 hasher.write(magic_hasher_line.data(), magic_hasher_line.size());
153
154 // Get the stored file format
155 std::string format_key;
156 std::getline(dump_file, format_key, ',');
157 std::string format_value;
158 std::getline(dump_file, format_value, '\n');
159 if (format_key != "format") {
160 error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
161 dump_file.close();
162 return false;
163 }
164 // Get the data file format with format_value as the default
165 std::string file_format = gArgs.GetArg("-format", format_value);
166 if (file_format.empty()) {
167 error = _("No wallet file format provided. To use createfromdump, -format=<format> must be provided.");
168 return false;
169 }
170 DatabaseFormat data_format;
171 if (file_format == "bdb") {
172 data_format = DatabaseFormat::BERKELEY;
173 } else if (file_format == "sqlite") {
174 data_format = DatabaseFormat::SQLITE;
175 } else {
176 error = strprintf(_("Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or \"sqlite\"."), file_format);
177 return false;
178 }
179 if (file_format != format_value) {
180 warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format));
181 }
182 std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
183 hasher.write(format_hasher_line.data(), format_hasher_line.size());
184
185 DatabaseOptions options;
186 DatabaseStatus status;
187 options.require_create = true;
188 options.require_format = data_format;
189 std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
190 if (!database) return false;
191
192 // dummy chain interface
193 bool ret = true;
194 std::shared_ptr<CWallet> wallet(new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet);
195 {
196 LOCK(wallet->cs_wallet);
197 DBErrors load_wallet_ret = wallet->LoadWallet();
198 if (load_wallet_ret != DBErrors::LOAD_OK) {
199 error = strprintf(_("Error creating %s"), name);
200 return false;
201 }
202
203 // Get the database handle
204 WalletDatabase& db = wallet->GetDatabase();
205 std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
206 batch->TxnBegin();
207
208 // Read the records from the dump file and write them to the database
209 while (dump_file.good()) {
210 std::string key;
211 std::getline(dump_file, key, ',');
212 std::string value;
213 std::getline(dump_file, value, '\n');
214
215 if (key == "checksum") {
216 std::vector<unsigned char> parsed_checksum = ParseHex(value);
217 std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
218 break;
219 }
220
221 std::string line = strprintf("%s,%s\n", key, value);
222 hasher.write(line.data(), line.size());
223
224 if (key.empty() || value.empty()) {
225 continue;
226 }
227
228 if (!IsHex(key)) {
229 error = strprintf(_("Error: Got key that was not hex: %s"), key);
230 ret = false;
231 break;
232 }
233 if (!IsHex(value)) {
234 error = strprintf(_("Error: Got value that was not hex: %s"), value);
235 ret = false;
236 break;
237 }
238
239 std::vector<unsigned char> k = ParseHex(key);
240 std::vector<unsigned char> v = ParseHex(value);
241
243 CDataStream ss_value(v, SER_DISK, CLIENT_VERSION);
244
245 if (!batch->Write(ss_key, ss_value)) {
246 error = strprintf(_("Error: Unable to write record to new wallet"));
247 ret = false;
248 break;
249 }
250 }
251
252 if (ret) {
253 uint256 comp_checksum = hasher.GetHash();
254 if (checksum.IsNull()) {
255 error = _("Error: Missing checksum");
256 ret = false;
257 } else if (checksum != comp_checksum) {
258 error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
259 ret = false;
260 }
261 }
262
263 if (ret) {
264 batch->TxnCommit();
265 } else {
266 batch->TxnAbort();
267 }
268
269 batch.reset();
270
271 dump_file.close();
272 }
273 wallet.reset(); // The pointer deleter will close the wallet for us.
274
275 // Remove the wallet dir if we have a failure
276 if (!ret) {
277 fs::remove_all(wallet_path);
278 }
279
280 return ret;
281}
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:590
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:101
void write(const char *pch, size_t size)
Definition: hash.h:114
uint256 GetHash()
Compute the double-SHA256 hash of all data written to this object.
Definition: hash.h:122
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:229
An instance of this class represents one database.
Definition: db.h:104
virtual std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true)=0
Make a DatabaseBatch connected to this database.
virtual std::string Format()=0
unsigned char * begin()
Definition: uint256.h:58
bool IsNull() const
Definition: uint256.h:31
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:34
256-bit opaque blob.
Definition: uint256.h:124
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:33
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1103
DatabaseStatus
Definition: db.h:212
DatabaseFormat
Definition: db.h:198
bool CreateFromDump(const std::string &name, const fs::path &wallet_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: dump.cpp:108
bool DumpWallet(CWallet &wallet, bilingual_str &error)
Definition: dump.cpp:13
static const std::string DUMP_MAGIC
Definition: dump.cpp:10
uint32_t DUMP_VERSION
Definition: dump.cpp:11
static void WalletToolReleaseWallet(CWallet *wallet)
Definition: dump.cpp:101
static bool exists(const path &p)
Definition: fs.h:77
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:120
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:133
fs::ofstream ofstream
Definition: fs.h:225
fs::ifstream ifstream
Definition: fs.h:224
void format(std::ostream &out, const char *fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1062
const char * name
Definition: rest.cpp:43
@ SER_DISK
Definition: serialize.h:139
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< unsigned char > ParseHex(const char *psz)
bool ParseUInt32(const std::string &str, uint32_t *out)
Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
bool IsHex(const std::string &str)
bool require_create
Definition: db.h:205
std::optional< DatabaseFormat > require_format
Definition: db.h:206
Bilingual messages:
Definition: translation.h:16
#define LOCK(cs)
Definition: sync.h:226
bool error(const char *fmt, const Args &... args)
Definition: system.h:49
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:63
ArgsManager gArgs
Definition: system.cpp:85
DBErrors
Error statuses for the wallet database.
Definition: walletdb.h:44