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