Bitcoin Core  21.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 
10 static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
11 uint32_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 = 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."), path.string());
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"), path.string());
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 
108 bool 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 = dump_filename;
118  dump_path = fs::absolute(dump_path);
119  if (!fs::exists(dump_path)) {
120  error = strprintf(_("Dump file %s does not exist."), dump_path.string());
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  bool first_run = true;
198  DBErrors load_wallet_ret = wallet->LoadWallet(first_run);
199  if (load_wallet_ret != DBErrors::LOAD_OK) {
200  error = strprintf(_("Error creating %s"), name);
201  return false;
202  }
203 
204  // Get the database handle
205  WalletDatabase& db = wallet->GetDatabase();
206  std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
207  batch->TxnBegin();
208 
209  // Read the records from the dump file and write them to the database
210  while (dump_file.good()) {
211  std::string key;
212  std::getline(dump_file, key, ',');
213  std::string value;
214  std::getline(dump_file, value, '\n');
215 
216  if (key == "checksum") {
217  std::vector<unsigned char> parsed_checksum = ParseHex(value);
218  std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
219  break;
220  }
221 
222  std::string line = strprintf("%s,%s\n", key, value);
223  hasher.write(line.data(), line.size());
224 
225  if (key.empty() || value.empty()) {
226  continue;
227  }
228 
229  if (!IsHex(key)) {
230  error = strprintf(_("Error: Got key that was not hex: %s"), key);
231  ret = false;
232  break;
233  }
234  if (!IsHex(value)) {
235  error = strprintf(_("Error: Got value that was not hex: %s"), value);
236  ret = false;
237  break;
238  }
239 
240  std::vector<unsigned char> k = ParseHex(key);
241  std::vector<unsigned char> v = ParseHex(value);
242 
243  CDataStream ss_key(k, SER_DISK, CLIENT_VERSION);
244  CDataStream ss_value(v, SER_DISK, CLIENT_VERSION);
245 
246  if (!batch->Write(ss_key, ss_value)) {
247  error = strprintf(_("Error: Unable to write record to new wallet"));
248  ret = false;
249  break;
250  }
251  }
252 
253  if (ret) {
254  uint256 comp_checksum = hasher.GetHash();
255  if (checksum.IsNull()) {
256  error = _("Error: Missing checksum");
257  ret = false;
258  } else if (checksum != comp_checksum) {
259  error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
260  ret = false;
261  }
262  }
263 
264  if (ret) {
265  batch->TxnCommit();
266  } else {
267  batch->TxnAbort();
268  }
269 
270  batch.reset();
271 
272  dump_file.close();
273  }
274  wallet.reset(); // The pointer deleter will close the wallet for us.
275 
276  // Remove the wallet dir if we have a failure
277  if (!ret) {
278  fs::remove_all(wallet_path);
279  }
280 
281  return ret;
282 }
static void WalletToolReleaseWallet(CWallet *wallet)
Definition: dump.cpp:101
void Close()
Close wallet database.
Definition: wallet.cpp:490
void write(const char *pch, size_t size)
Definition: hash.h:114
bool DumpWallet(CWallet &wallet, bilingual_str &error)
Definition: dump.cpp:13
bool require_create
Definition: db.h:206
Bilingual messages:
Definition: translation.h:16
static const std::string DUMP_MAGIC
Definition: dump.cpp:10
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
bool CreateFromDump(const std::string &name, const fs::path &wallet_path, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: dump.cpp:108
bool ParseUInt32(const std::string &str, uint32_t *out)
Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
std::vector< unsigned char > ParseHex(const char *psz)
fs::ifstream ifstream
Definition: fs.h:101
virtual std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true)=0
Make a DatabaseBatch connected to this database.
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:202
fs::ofstream ofstream
Definition: fs.h:102
DBErrors
Error statuses for the wallet database.
Definition: walletdb.h:43
unsigned char * begin()
Definition: uint256.h:58
bool IsNull() const
Definition: uint256.h:31
void WalletLogPrintf(std::string fmt, Params... parameters) const
Prepends the wallet name in logging output to ease debugging in multi-wallet use cases.
Definition: wallet.h:1190
#define LOCK(cs)
Definition: sync.h:232
const char * name
Definition: rest.cpp:41
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:57
virtual std::string Format()=0
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
bool IsHex(const std::string &str)
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
uint256 GetHash()
Compute the double-SHA256 hash of all data written to this object.
Definition: hash.h:122
256-bit opaque blob.
Definition: uint256.h:124
WalletDatabase & GetDatabase() const override
Definition: wallet.h:732
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:467
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:622
ArgsManager gArgs
Definition: system.cpp:77
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:100
Optional< DatabaseFormat > require_format
Definition: db.h:207
DatabaseStatus
Definition: db.h:213
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
uint32_t DUMP_VERSION
Definition: dump.cpp:11
DatabaseFormat
Definition: db.h:199
bool error(const char *fmt, const Args &... args)
Definition: system.h:52
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Definition: walletdb.cpp:1003
An instance of this class represents one database.
Definition: db.h:104