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