Electroneum
simplewallet.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31 
38 // use boost bind placeholders for now
39 #define BOOST_BIND_GLOBAL_PLACEHOLDERS 1
40 #include <boost/bind.hpp>
41 
42 #include <thread>
43 #include <iostream>
44 #include <sstream>
45 #include <fstream>
46 #include <ctype.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/program_options.hpp>
49 #include <boost/algorithm/string.hpp>
50 #include <boost/format.hpp>
51 #include <boost/regex.hpp>
52 #include <boost/range/adaptor/transformed.hpp>
53 #include "include_base_utils.h"
54 #include "common/i18n.h"
55 #include "common/command_line.h"
56 #include "common/util.h"
57 #include "common/dns_utils.h"
58 #include "common/base58.h"
61 #include "simplewallet.h"
65 #include "crypto/crypto.h" // for crypto::secret_key definition
67 #include "rapidjson/document.h"
68 #include "common/json_util.h"
69 #include "ringct/rctSigs.h"
70 #include "multisig/multisig.h"
71 #include "wallet/wallet_args.h"
72 #include "version.h"
73 #include <stdexcept>
74 #include "wallet/message_store.h"
76 
77 #ifdef WIN32
78 #include <boost/locale.hpp>
79 #include <boost/filesystem.hpp>
80 #endif
81 
82 #ifdef HAVE_READLINE
83 #include "readline_buffer.h"
84 #endif
85 
86 using namespace std;
87 using namespace epee;
88 using namespace cryptonote;
89 using boost::lexical_cast;
90 namespace po = boost::program_options;
92 
93 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
94 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.simplewallet"
95 
96 #define EXTENDED_LOGS_FILE "wallet_details.log"
97 
98 #define DEFAULT_MIX 0
99 
100 #define MIN_RING_SIZE 1 // Used to inform user about min ring size -- does not track actual protocol
101 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
102 
103 #define LOCK_IDLE_SCOPE() \
104  bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
105  m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
106  /* stop any background refresh, and take over */ \
107  m_wallet->stop(); \
108  boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
109  m_idle_cond.notify_all(); \
110  epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
111  m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
112  })
113 
114 #define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \
115  LOCK_IDLE_SCOPE(); \
116  boost::optional<tools::password_container> pwd_container = boost::none; \
117  if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \
118  tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
119 
120 #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;)
121 
122 #define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help;
123 
124 #define LONG_PAYMENT_ID_SUPPORT_CHECK() \
125  do { \
126  if (!m_long_payment_id_support) { \
127  fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \
128  fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \
129  fail_msg_writer() << tr("Use --long-payment-id-support-bad-for-privacy if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \
130  return true; \
131  } \
132  } while(0)
133 
137 };
138 
139 namespace
140 {
141  const std::array<const char* const, 5> allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}};
143  const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
144  const command_line::arg_descriptor<std::string> arg_generate_from_device = {"generate-from-device", sw::tr("Generate new wallet from device and save it to <arg>"), ""};
145  const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
146  const command_line::arg_descriptor<std::string> arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""};
147  const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
148  const command_line::arg_descriptor<std::string> arg_generate_from_multisig_keys = {"generate-from-multisig-keys", sw::tr("Generate a master wallet from multisig wallet keys"), ""};
150  const command_line::arg_descriptor<std::string> arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""};
151  const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
152  const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
153  const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
154  const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false};
155  const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
156  const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0};
157  const command_line::arg_descriptor<std::string> arg_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""};
158  const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the electroneum network"), false};
159  const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false};
160  const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""};
161  const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false};
162  const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support-bad-for-privacy", sw::tr("Support obsolete long (unencrypted) payment ids (using them harms your privacy)"), true};
163  const command_line::arg_descriptor<uint64_t> fallback_to_pow_checkpoint_height = {"fallback-to-pow-checkpoint-height", tools::wallet2::tr("Warning: This is to set the height for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), 0, true};
164  const command_line::arg_descriptor<std::string> fallback_to_pow_checkpoint_hash = {"fallback-to-pow-checkpoint-hash", tools::wallet2::tr("Warning: This is to set the hash for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), "", true};
165 
167 
168  const command_line::arg_descriptor<uint32_t> arg_account_major_offset = {"account-major-offset", sw::tr("Account Index Offset"), 0};
169 
170  const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
171  const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]");
172  const char* USAGE_SHOW_BALANCE("balance [detail]");
173  const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
174  const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
175  const char* USAGE_PAYMENT_ID("payment_id");
176  const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
177  const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
178  const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
179  const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
180  const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
181  const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
182  const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]");
183  const char* USAGE_SIGN_TRANSFER("sign_transfer [export_raw]");
184  const char* USAGE_SET_LOG("set_log <level>|{+,-,}<categories>");
185  const char* USAGE_ACCOUNT("account\n"
186  " account new <label text with white spaces allowed>\n"
187  " account switch <index> \n"
188  " account label <index> <label text with white spaces allowed>\n"
189  " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
190  " account untag <account_index_1> [<account_index_2> ...]\n"
191  " account tag_description <tag_name> <description>");
192  const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]");
193  const char* USAGE_INTEGRATED_ADDRESS("integrated_address [<payment_id> | <address>]");
194  const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
195  const char* USAGE_SET_VARIABLE("set <option> [<value>]");
196  const char* USAGE_GET_TX_KEY("get_tx_key <txid>");
197  const char* USAGE_SET_TX_KEY("set_tx_key <txid> <tx_key>");
198  const char* USAGE_CHECK_TX_KEY("check_tx_key <txid> <txkey> <address>");
199  const char* USAGE_GET_TX_PROOF("get_tx_proof <txid> <address> [<message>]");
200  const char* USAGE_CHECK_TX_PROOF("check_tx_proof <txid> <address> <signature_file> [<message>]");
201  const char* USAGE_GET_SPEND_PROOF("get_spend_proof <txid> [<message>]");
202  const char* USAGE_CHECK_SPEND_PROOF("check_spend_proof <txid> <signature_file> [<message>]");
203  const char* USAGE_GET_RESERVE_PROOF("get_reserve_proof (all|<amount>) [<message>]");
204  const char* USAGE_CHECK_RESERVE_PROOF("check_reserve_proof <address> <signature_file> [<message>]");
205  const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
206  const char* USAGE_UNSPENT_OUTPUTS("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]");
207  const char* USAGE_RESCAN_BC("rescan_bc [hard|soft|keep_ki] [start_height=0]");
208  const char* USAGE_SET_TX_NOTE("set_tx_note <txid> [free text note]");
209  const char* USAGE_GET_TX_NOTE("get_tx_note <txid>");
210  const char* USAGE_GET_DESCRIPTION("get_description");
211  const char* USAGE_SET_DESCRIPTION("set_description [free text note]");
212  const char* USAGE_SIGN("sign <filename>");
213  const char* USAGE_VERIFY("verify <filename> <address> <signature>");
214  const char* USAGE_EXPORT_KEY_IMAGES("export_key_images <filename>");
215  const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>");
216  const char* USAGE_HW_KEY_IMAGES_SYNC("hw_key_images_sync");
217  const char* USAGE_HW_RECONNECT("hw_reconnect");
218  const char* USAGE_EXPORT_OUTPUTS("export_outputs <filename>");
219  const char* USAGE_IMPORT_OUTPUTS("import_outputs <filename>");
220  const char* USAGE_SHOW_TRANSFER("show_transfer <txid>");
221  const char* USAGE_MAKE_MULTISIG("make_multisig <threshold> <string1> [<string>...]");
222  const char* USAGE_FINALIZE_MULTISIG("finalize_multisig <string> [<string>...]");
223  const char* USAGE_EXCHANGE_MULTISIG_KEYS("exchange_multisig_keys <string> [<string>...]");
224  const char* USAGE_EXPORT_MULTISIG_INFO("export_multisig_info <filename>");
225  const char* USAGE_IMPORT_MULTISIG_INFO("import_multisig_info <filename> [<filename>...]");
226  const char* USAGE_SIGN_MULTISIG("sign_multisig <filename>");
227  const char* USAGE_SUBMIT_MULTISIG("submit_multisig <filename>");
228  const char* USAGE_EXPORT_RAW_MULTISIG_TX("export_raw_multisig_tx <filename>");
229  const char* USAGE_MMS("mms [<subcommand> [<subcommand_parameters>]]");
230  const char* USAGE_MMS_INIT("mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
231  const char* USAGE_MMS_INFO("mms info");
232  const char* USAGE_MMS_SIGNER("mms signer [<number> <label> [<transport_address> [<etn_address>]]]");
233  const char* USAGE_MMS_LIST("mms list");
234  const char* USAGE_MMS_NEXT("mms next [sync]");
235  const char* USAGE_MMS_SYNC("mms sync");
236  const char* USAGE_MMS_TRANSFER("mms transfer <transfer_command_arguments>");
237  const char* USAGE_MMS_DELETE("mms delete (<message_id> | all)");
238  const char* USAGE_MMS_SEND("mms send [<message_id>]");
239  const char* USAGE_MMS_RECEIVE("mms receive");
240  const char* USAGE_MMS_EXPORT("mms export <message_id>");
241  const char* USAGE_MMS_NOTE("mms note [<label> <text>]");
242  const char* USAGE_MMS_SHOW("mms show <message_id>");
243  const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]");
244  const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config");
245  const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]");
246  const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config");
247  const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>");
248  const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>");
249  const char* USAGE_SET_RING("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )");
250  const char* USAGE_UNSET_RING("unset_ring <txid> | ( <key_image> [<key_image>...] )");
251  const char* USAGE_SAVE_KNOWN_RINGS("save_known_rings");
252  const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]");
253  const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>");
254  const char* USAGE_IS_OUTPUT_SPENT("is_output_spent <amount>/<offset>");
255  const char* USAGE_FREEZE("freeze <key_image>");
256  const char* USAGE_THAW("thaw <key_image>");
257  const char* USAGE_FROZEN("frozen <key_image>");
258  const char* USAGE_NET_STATS("net_stats");
259  const char* USAGE_WELCOME("welcome");
260  const char* USAGE_VERSION("version");
261  const char* USAGE_HELP("help [<command>]");
262 
263  std::string input_line(const std::string& prompt, bool yesno = false)
264  {
265 #ifdef HAVE_READLINE
266  rdln::suspend_readline pause_readline;
267 #endif
268  std::cout << prompt;
269  if (yesno)
270  std::cout << " (Y/Yes/N/No)";
271  std::cout << ": " << std::flush;
272 
274 #ifdef _WIN32
275  buf = tools::input_line_win();
276 #else
277  std::getline(std::cin, buf);
278 #endif
279 
281  }
282 
283  epee::wipeable_string input_secure_line(const char *prompt)
284  {
285 #ifdef HAVE_READLINE
286  rdln::suspend_readline pause_readline;
287 #endif
288  auto pwd_container = tools::password_container::prompt(false, prompt, false);
289  if (!pwd_container)
290  {
291  MERROR("Failed to read secure line");
292  return "";
293  }
294 
295  epee::wipeable_string buf = pwd_container->password();
296 
297  buf.trim();
298  return buf;
299  }
300 
301  boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
302  {
303 #ifdef HAVE_READLINE
304  rdln::suspend_readline pause_readline;
305 #endif
306  auto pwd_container = tools::password_container::prompt(verify, prompt);
307  if (!pwd_container)
308  {
309  tools::fail_msg_writer() << sw::tr("failed to read wallet password");
310  }
311  return pwd_container;
312  }
313 
314  boost::optional<tools::password_container> default_password_prompter(bool verify)
315  {
316  return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify);
317  }
318 
319  inline std::string interpret_rpc_response(bool ok, const std::string& status)
320  {
321  std::string err;
322  if (ok)
323  {
324  if (status == CORE_RPC_STATUS_BUSY)
325  {
326  err = sw::tr("daemon is busy. Please try again later.");
327  }
328  else if (status != CORE_RPC_STATUS_OK)
329  {
330  err = status;
331  }
332  }
333  else
334  {
335  err = sw::tr("possibly lost connection to daemon");
336  }
337  return err;
338  }
339 
341  {
343  }
344 
345  tools::scoped_message_writer message_writer(epee::console_colors color = epee::console_color_default, bool bright = false)
346  {
347  return tools::scoped_message_writer(color, bright);
348  }
349 
351  {
353  }
354 
355  bool parse_bool(const std::string& s, bool& result)
356  {
357  if (s == "1" || command_line::is_yes(s))
358  {
359  result = true;
360  return true;
361  }
362  if (s == "0" || command_line::is_no(s))
363  {
364  result = false;
365  return true;
366  }
367 
368  boost::algorithm::is_iequal ignore_case{};
369  if (boost::algorithm::equals("true", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case))
370  {
371  result = true;
372  return true;
373  }
374  if (boost::algorithm::equals("false", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("false"), s, ignore_case))
375  {
376  result = false;
377  return true;
378  }
379 
380  return false;
381  }
382 
383  template <typename F>
384  bool parse_bool_and_use(const std::string& s, F func)
385  {
386  bool r;
387  if (parse_bool(s, r))
388  {
389  func(r);
390  return true;
391  }
392  else
393  {
394  fail_msg_writer() << sw::tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
395  return false;
396  }
397  }
398 
399  const struct
400  {
401  const char *name;
402  tools::wallet2::RefreshType refresh_type;
403  } refresh_type_names[] =
404  {
405  { "full", tools::wallet2::RefreshFull },
406  { "optimize-coinbase", tools::wallet2::RefreshOptimizeCoinbase },
407  { "optimized-coinbase", tools::wallet2::RefreshOptimizeCoinbase },
408  { "no-coinbase", tools::wallet2::RefreshNoCoinbase },
409  { "default", tools::wallet2::RefreshDefault },
410  };
411 
412  bool parse_refresh_type(const std::string &s, tools::wallet2::RefreshType &refresh_type)
413  {
414  for (size_t n = 0; n < sizeof(refresh_type_names) / sizeof(refresh_type_names[0]); ++n)
415  {
416  if (s == refresh_type_names[n].name)
417  {
418  refresh_type = refresh_type_names[n].refresh_type;
419  return true;
420  }
421  }
422  fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse refresh type");
423  return false;
424  }
425 
426  std::string get_refresh_type_name(tools::wallet2::RefreshType type)
427  {
428  for (size_t n = 0; n < sizeof(refresh_type_names) / sizeof(refresh_type_names[0]); ++n)
429  {
430  if (type == refresh_type_names[n].refresh_type)
431  return refresh_type_names[n].name;
432  }
433  return "invalid";
434  }
435 
436  std::string get_version_string(uint32_t version)
437  {
438  return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff);
439  }
440 
441  std::string oa_prompter(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)
442  {
443  if (addresses.empty())
444  return {};
445  // prompt user for confirmation.
446  // inform user of DNSSEC validation status as well.
447  std::string dnssec_str;
448  if (dnssec_valid)
449  {
450  dnssec_str = sw::tr("DNSSEC validation passed");
451  }
452  else
453  {
454  dnssec_str = sw::tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
455  }
456  std::stringstream prompt;
457  prompt << sw::tr("For URL: ") << url
458  << ", " << dnssec_str << std::endl
459  << sw::tr(" Electroneum Address = ") << addresses[0]
460  << std::endl
461  << sw::tr("Is this OK?")
462  ;
463  // prompt the user for confirmation given the dns query and dnssec status
464  std::string confirm_dns_ok = input_line(prompt.str(), true);
465  if (std::cin.eof())
466  {
467  return {};
468  }
469  if (!command_line::is_yes(confirm_dns_ok))
470  {
471  std::cout << sw::tr("you have cancelled the transfer request") << std::endl;
472  return {};
473  }
474  return addresses[0];
475  }
476 
477  bool parse_subaddress_indices(const std::string& arg, std::set<uint32_t>& subaddr_indices)
478  {
479  subaddr_indices.clear();
480 
481  if (arg.substr(0, 6) != "index=")
482  return false;
483  std::string subaddr_indices_str_unsplit = arg.substr(6, arg.size() - 6);
484  std::vector<std::string> subaddr_indices_str;
485  boost::split(subaddr_indices_str, subaddr_indices_str_unsplit, boost::is_any_of(","));
486 
487  for (const auto& subaddr_index_str : subaddr_indices_str)
488  {
489  uint32_t subaddr_index;
490  if(!epee::string_tools::get_xtype_from_string(subaddr_index, subaddr_index_str))
491  {
492  fail_msg_writer() << sw::tr("failed to parse index: ") << subaddr_index_str;
493  subaddr_indices.clear();
494  return false;
495  }
496  subaddr_indices.insert(subaddr_index);
497  }
498  return true;
499  }
500 
501  boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
502  {
503  auto r = tools::parse_subaddress_lookahead(str);
504  if (!r)
505  fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be <major>:<minor>");
506  return r;
507  }
508 
509  void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon)
510  {
511  bool warn_of_possible_attack = !trusted_daemon;
512  try
513  {
514  std::rethrow_exception(e);
515  }
516  catch (const tools::error::daemon_busy&)
517  {
518  fail_msg_writer() << sw::tr("daemon is busy. Please try again later.");
519  }
521  {
522  fail_msg_writer() << sw::tr("no connection to daemon. Please make sure daemon is running.");
523  }
524  catch (const tools::error::wallet_rpc_error& e)
525  {
526  LOG_ERROR("RPC error: " << e.to_string());
527  fail_msg_writer() << sw::tr("RPC error: ") << e.what();
528  }
529  catch (const tools::error::get_outs_error &e)
530  {
531  fail_msg_writer() << sw::tr("failed to get random outputs to mix: ") << e.what();
532  }
534  {
535  LOG_PRINT_L0(boost::format("not enough ETN to transfer, available only %s, sent amount %s") %
536  print_etn(e.available()) %
537  print_etn(e.tx_amount()));
538  fail_msg_writer() << sw::tr("Not enough ETN in unlocked balance");
539  warn_of_possible_attack = false;
540  }
541  catch (const tools::error::not_enough_etn& e)
542  {
543  LOG_PRINT_L0(boost::format("not enough ETN to transfer, available only %s, sent amount %s") %
544  print_etn(e.available()) %
545  print_etn(e.tx_amount()));
546  fail_msg_writer() << sw::tr("Not enough ETN in unlocked balance");
547  warn_of_possible_attack = false;
548  }
549  catch (const tools::error::tx_not_possible& e)
550  {
551  LOG_PRINT_L0(boost::format("not enough ETN to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
552  print_etn(e.available()) %
553  print_etn(e.tx_amount() + e.fee()) %
554  print_etn(e.tx_amount()) %
555  print_etn(e.fee()));
556  fail_msg_writer() << sw::tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more ETN than the unlocked balance, or not leaving enough for fees");
557  warn_of_possible_attack = false;
558  }
559  catch (const tools::error::not_enough_outs_to_mix& e)
560  {
561  auto writer = fail_msg_writer();
562  writer << sw::tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
563  for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
564  {
565  writer << "\n" << sw::tr("output amount") << " = " << print_etn(outs_for_amount.first) << ", " << sw::tr("found outputs to use") << " = " << outs_for_amount.second;
566  }
567  writer << sw::tr("Please use sweep_unmixable.");
568  }
569  catch (const tools::error::tx_not_constructed&)
570  {
571  fail_msg_writer() << sw::tr("transaction was not constructed");
572  warn_of_possible_attack = false;
573  }
574  catch (const tools::error::tx_rejected& e)
575  {
576  fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon")) % get_transaction_hash(e.tx()));
577  std::string reason = e.reason();
578  if (!reason.empty())
579  fail_msg_writer() << sw::tr("Reason: ") << reason;
580  }
581  catch (const tools::error::tx_sum_overflow& e)
582  {
583  fail_msg_writer() << e.what();
584  warn_of_possible_attack = false;
585  }
586  catch (const tools::error::zero_destination&)
587  {
588  fail_msg_writer() << sw::tr("one of destinations is zero");
589  warn_of_possible_attack = false;
590  }
591  catch (const tools::error::tx_too_big& e)
592  {
593  fail_msg_writer() << sw::tr("failed to find a suitable way to split transactions");
594  warn_of_possible_attack = false;
595  }
596  catch (const tools::error::transfer_error& e)
597  {
598  LOG_ERROR("unknown transfer error: " << e.to_string());
599  fail_msg_writer() << sw::tr("unknown transfer error: ") << e.what();
600  }
601  catch (const tools::error::multisig_export_needed& e)
602  {
603  LOG_ERROR("Multisig error: " << e.to_string());
604  fail_msg_writer() << sw::tr("Multisig error: ") << e.what();
605  warn_of_possible_attack = false;
606  }
607  catch (const tools::error::wallet_internal_error& e)
608  {
609  LOG_ERROR("internal error: " << e.to_string());
610  fail_msg_writer() << sw::tr("internal error: ") << e.what();
611  }
612  catch (const std::exception& e)
613  {
614  LOG_ERROR("unexpected error: " << e.what());
615  fail_msg_writer() << sw::tr("unexpected error: ") << e.what();
616  }
617 
618  if (warn_of_possible_attack)
619  fail_msg_writer() << sw::tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
620  }
621 
622  bool check_file_overwrite(const std::string &filename)
623  {
624  boost::system::error_code errcode;
625  if (boost::filesystem::exists(filename, errcode))
626  {
627  if (boost::ends_with(filename, ".keys"))
628  {
629  fail_msg_writer() << boost::format(sw::tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
630  return false;
631  }
632  return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it?")) % filename).str(), true));
633  }
634  return true;
635  }
636 
637  void print_secret_key(const crypto::secret_key &k)
638  {
639  static constexpr const char hex[] = u8"0123456789abcdef";
640  const uint8_t *ptr = (const uint8_t*)k.data;
641  for (size_t i = 0, sz = sizeof(k); i < sz; ++i)
642  {
643  putchar(hex[*ptr >> 4]);
644  putchar(hex[*ptr & 15]);
645  ++ptr;
646  }
647  }
648 }
649 
650 bool parse_priority(const std::string& arg, uint32_t& priority)
651 {
652  auto priority_pos = std::find(
653  allowed_priority_strings.begin(),
654  allowed_priority_strings.end(),
655  arg);
656  if(priority_pos != allowed_priority_strings.end()) {
657  priority = std::distance(allowed_priority_strings.begin(), priority_pos);
658  return true;
659  }
660  return false;
661 }
662 
663 std::string join_priority_strings(const char *delimiter)
664 {
665  std::string s;
666  for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
667  {
668  if (!s.empty())
669  s += delimiter;
670  s += allowed_priority_strings[n];
671  }
672  return s;
673 }
674 
675 std::string simple_wallet::get_commands_str()
676 {
677  std::stringstream ss;
678  ss << tr("Commands: ") << ENDL;
679  std::string usage = m_cmd_binder.get_usage();
680  boost::replace_all(usage, "\n", "\n ");
681  usage.insert(0, " ");
682  ss << usage << ENDL;
683  return ss.str();
684 }
685 
686 std::string simple_wallet::get_command_usage(const std::vector<std::string> &args)
687 {
688  std::pair<std::string, std::string> documentation = m_cmd_binder.get_documentation(args);
689  std::stringstream ss;
690  if(documentation.first.empty())
691  {
692  ss << tr("Unknown command: ") << args.front();
693  }
694  else
695  {
696  std::string usage = documentation.second.empty() ? args.front() : documentation.first;
697  std::string description = documentation.second.empty() ? documentation.first : documentation.second;
698  usage.insert(0, " ");
699  ss << tr("Command usage: ") << ENDL << usage << ENDL << ENDL;
700  boost::replace_all(description, "\n", "\n ");
701  description.insert(0, " ");
702  ss << tr("Command description: ") << ENDL << description << ENDL;
703  }
704  return ss.str();
705 }
706 
707 bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
708 {
709  // don't log
710  PAUSE_READLINE();
711  if (m_wallet->key_on_device()) {
712  std::cout << "secret: On device. Not available" << std::endl;
713  } else {
715  printf("secret: ");
716  print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
717  putchar('\n');
718  }
719  std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
720 
721  return true;
722 }
723 
724 bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
725 {
726  if (m_wallet->watch_only())
727  {
728  fail_msg_writer() << tr("wallet is watch-only and has no spend key");
729  return true;
730  }
731  // don't log
732  PAUSE_READLINE();
733  if (m_wallet->key_on_device()) {
734  std::cout << "secret: On device. Not available" << std::endl;
735  } else {
737  printf("secret: ");
738  print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
739  putchar('\n');
740 
741  // SMARTCHAIN ADDRESS
742  unsigned char seckey1[32];
743  unsigned char public_key64[65];
744  size_t pk_len = 65;
745  secp256k1_pubkey pubkey1;
747  memcpy(seckey1, m_wallet->get_account().get_keys().m_spend_secret_key.data, 32);
748  if(secp256k1_ec_seckey_verify(ctx, seckey1) == 0) { // sec key has an unrealistic chance of being invalid (10^-128) https://en.bitcoin.it/wiki/Private_key
749  LOG_ERROR("Invalid private key");
750  return false;
751  }
752 
753  // create the pubkey and serialise it
754  if(secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1) == 0) { // this format is not sufficient for hashing, hence serialisation
755  LOG_ERROR("Failed to create secp256k1 public key");
756  return false;
757  }
758  secp256k1_ec_pubkey_serialize(ctx, public_key64, &pk_len, &pubkey1, SECP256K1_EC_UNCOMPRESSED); // serialise pubkey1 into publickey_64
759  std::string long_public_key2 = epee::string_tools::pod_to_hex(public_key64); // debug purposes - can check against https://lab.miguelmota.com/ethereum-private-key-to-public-key/example/
760 
761  // Ethereum address generation: Take the last 20 bytes of the Keccak-256 hash of the public key
762  // keccak-1600() is not suitable, but keccak() with 24 rounds and mdlen (=size) of 32 is the same
763  // as keccak-256 with a 32 byte output. 24 rounds is the default in Monero for keccak()
764  // the first byte is the compression type so hash the 64 bytes after the first byte only
765  // I have put the 32 byte hash inside pubkey1.data just to save time
766  keccak(public_key64 + 1, 64, pubkey1.data, 32);
767  unsigned char address[20]; //smartchain address
768  memcpy(address, pubkey1.data + 12, 20); // take the last 20 bytes of the 32 byte array for the address
769  std::string hex_address = epee::string_tools::pod_to_hex(address); // should be 0x12ed7467c3852e6b2Bd3C22AF694be8DF7637B10.
770  std::string bridge_smartchain_address = "0x" + hex_address; //prefix address with 0x
771  LOG_PRINT_L1("Smartchain address: " << bridge_smartchain_address);
772 
773  std::cout << "smartchain address: " << bridge_smartchain_address << std::endl;
774  }
775  std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
776 
777  return true;
778 }
779 
780 bool simple_wallet::print_seed(bool encrypted)
781 {
782  bool success = false;
784  bool ready, multisig;
785 
786  if (m_wallet->key_on_device())
787  {
788  fail_msg_writer() << tr("command not supported by HW wallet");
789  return true;
790  }
791  if (m_wallet->watch_only())
792  {
793  fail_msg_writer() << tr("wallet is watch-only and has no seed");
794  return true;
795  }
796 
797  multisig = m_wallet->multisig(&ready);
798  if (multisig)
799  {
800  if (!ready)
801  {
802  fail_msg_writer() << tr("wallet is multisig but not yet finalized");
803  return true;
804  }
805  }
806 
808 
809  if (!multisig && !m_wallet->is_deterministic())
810  {
811  fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
812  return true;
813  }
814 
815  epee::wipeable_string seed_pass;
816  if (encrypted)
817  {
818  auto pwd_container = password_prompter(tr("Enter optional seed offset passphrase, empty to see raw seed"), true);
819  if (std::cin.eof() || !pwd_container)
820  return true;
821  seed_pass = pwd_container->password();
822  }
823 
824  if (multisig)
825  success = m_wallet->get_multisig_seed(seed, seed_pass);
826  else if (m_wallet->is_deterministic())
827  success = m_wallet->get_seed(seed, seed_pass);
828 
829  if (success)
830  {
831  print_seed(seed);
832  }
833  else
834  {
835  fail_msg_writer() << tr("Failed to retrieve seed");
836  }
837  return true;
838 }
839 
840 bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
841 {
842  return print_seed(false);
843 }
844 
845 bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
846 {
847  return print_seed(true);
848 }
849 
850 bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
851 {
852  if (m_wallet->key_on_device())
853  {
854  fail_msg_writer() << tr("command not supported by HW wallet");
855  return true;
856  }
857  if (m_wallet->multisig())
858  {
859  fail_msg_writer() << tr("wallet is multisig and has no seed");
860  return true;
861  }
862  if (m_wallet->watch_only())
863  {
864  fail_msg_writer() << tr("wallet is watch-only and has no seed");
865  return true;
866  }
867 
868  epee::wipeable_string password;
869  {
871 
872  if (!m_wallet->is_deterministic())
873  {
874  fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
875  return true;
876  }
877 
878  // we need the password, even if ask-password is unset
879  if (!pwd_container)
880  {
881  pwd_container = get_and_verify_password();
882  if (pwd_container == boost::none)
883  {
884  fail_msg_writer() << tr("Incorrect password");
885  return true;
886  }
887  }
888  password = pwd_container->password();
889  }
890 
891  std::string mnemonic_language = get_mnemonic_language();
892  if (mnemonic_language.empty())
893  return true;
894 
895  m_wallet->set_seed_language(std::move(mnemonic_language));
896  m_wallet->rewrite(m_wallet_file, password);
897  return true;
898 }
899 
900 bool simple_wallet::change_password(const std::vector<std::string> &args)
901 {
902  const auto orig_pwd_container = get_and_verify_password();
903 
904  if(orig_pwd_container == boost::none)
905  {
906  fail_msg_writer() << tr("Your original password was incorrect.");
907  return true;
908  }
909 
910  // prompts for a new password, pass true to verify the password
911  const auto pwd_container = default_password_prompter(true);
912  if(!pwd_container)
913  return true;
914 
915  try
916  {
917  m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
918  }
919  catch (const tools::error::wallet_logic_error& e)
920  {
921  fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what();
922  return true;
923  }
924 
925  return true;
926 }
927 
928 bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
929 {
931 
932  crypto::hash payment_id;
933  if (args.size() > 0)
934  {
935  PRINT_USAGE(USAGE_PAYMENT_ID);
936  return true;
937  }
938  payment_id = crypto::rand<crypto::hash>();
939  success_msg_writer() << tr("Random payment ID: ") << payment_id;
940  return true;
941 }
942 
943 bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
944 {
945  if (!try_connect_to_daemon())
946  return true;
947  const bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE);
948  const uint64_t base_fee = m_wallet->get_base_fee();
949  const char *base = per_byte ? "byte" : "kB";
950  message_writer() << (boost::format(tr("Current fee is %s %s per %s")) % print_etn(base_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % base).str();
951 
952  // Display the calculated fees for various priorities
953  for (uint32_t priority = 1; priority <= 4; ++priority)
954  {
955  uint64_t mult = m_wallet->get_fee_multiplier(priority);
956  uint64_t fee = base_fee * (per_byte ? 2500 : 13) * mult; // typical_size is 2500 for per_byte and 13 for not per_byte
957  message_writer() << (boost::format(tr("Fee for priority %u: %s %s")) % priority % print_etn(fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point())).str();
958  }
959 
960  return true;
961 }
962 
963 bool simple_wallet::prepare_multisig(const std::vector<std::string> &args)
964 {
965  prepare_multisig_main(args, false);
966  return true;
967 }
968 
969 bool simple_wallet::prepare_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
970 {
971  if (m_wallet->key_on_device())
972  {
973  fail_msg_writer() << tr("command not supported by HW wallet");
974  return false;
975  }
976  if (m_wallet->multisig())
977  {
978  fail_msg_writer() << tr("This wallet is already multisig");
979  return false;
980  }
981  if (m_wallet->watch_only())
982  {
983  fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig");
984  return false;
985  }
986 
987  if(m_wallet->get_num_transfer_details())
988  {
989  fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet");
990  return false;
991  }
992 
994 
995  std::string multisig_info = m_wallet->get_multisig_info();
996  success_msg_writer() << multisig_info;
997  success_msg_writer() << tr("Send this multisig info to all other participants, then use make_multisig <threshold> <info1> [<info2>...] with others' multisig info");
998  success_msg_writer() << tr("This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants ");
999 
1000  if (called_by_mms)
1001  {
1002  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::key_set, multisig_info);
1003  }
1004 
1005  return true;
1006 }
1007 
1008 bool simple_wallet::make_multisig(const std::vector<std::string> &args)
1009 {
1010  make_multisig_main(args, false);
1011  return true;
1012 }
1013 
1014 bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
1015 {
1016  if (m_wallet->key_on_device())
1017  {
1018  fail_msg_writer() << tr("command not supported by HW wallet");
1019  return false;
1020  }
1021  if (m_wallet->multisig())
1022  {
1023  fail_msg_writer() << tr("This wallet is already multisig");
1024  return false;
1025  }
1026  if (m_wallet->watch_only())
1027  {
1028  fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig");
1029  return false;
1030  }
1031 
1032  if(m_wallet->get_num_transfer_details())
1033  {
1034  fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet");
1035  return false;
1036  }
1037 
1038  if (args.size() < 2)
1039  {
1040  PRINT_USAGE(USAGE_MAKE_MULTISIG);
1041  return false;
1042  }
1043 
1044  // parse threshold
1047  {
1048  fail_msg_writer() << tr("Invalid threshold");
1049  return false;
1050  }
1051 
1052  const auto orig_pwd_container = get_and_verify_password();
1053  if(orig_pwd_container == boost::none)
1054  {
1055  fail_msg_writer() << tr("Your original password was incorrect.");
1056  return false;
1057  }
1058 
1059  LOCK_IDLE_SCOPE();
1060 
1061  try
1062  {
1063  auto local_args = args;
1064  local_args.erase(local_args.begin());
1065  std::string multisig_extra_info = m_wallet->make_multisig(orig_pwd_container->password(), local_args, threshold);
1066  if (!multisig_extra_info.empty())
1067  {
1068  success_msg_writer() << tr("Another step is needed");
1069  success_msg_writer() << multisig_extra_info;
1070  success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
1071  if (called_by_mms)
1072  {
1073  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::additional_key_set, multisig_extra_info);
1074  }
1075  return true;
1076  }
1077  }
1078  catch (const std::exception &e)
1079  {
1080  fail_msg_writer() << tr("Error creating multisig: ") << e.what();
1081  return false;
1082  }
1083 
1084  uint32_t total;
1085  if (!m_wallet->multisig(NULL, &threshold, &total))
1086  {
1087  fail_msg_writer() << tr("Error creating multisig: new wallet is not multisig");
1088  return false;
1089  }
1090  success_msg_writer() << std::to_string(threshold) << "/" << total << tr(" multisig address: ")
1091  << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
1092 
1093  return true;
1094 }
1095 
1096 bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
1097 {
1098  bool ready;
1099  if (m_wallet->key_on_device())
1100  {
1101  fail_msg_writer() << tr("command not supported by HW wallet");
1102  return true;
1103  }
1104 
1105  const auto pwd_container = get_and_verify_password();
1106  if(pwd_container == boost::none)
1107  {
1108  fail_msg_writer() << tr("Your original password was incorrect.");
1109  return true;
1110  }
1111 
1112  if (!m_wallet->multisig(&ready))
1113  {
1114  fail_msg_writer() << tr("This wallet is not multisig");
1115  return true;
1116  }
1117  if (ready)
1118  {
1119  fail_msg_writer() << tr("This wallet is already finalized");
1120  return true;
1121  }
1122 
1123  LOCK_IDLE_SCOPE();
1124 
1125  if (args.size() < 2)
1126  {
1127  PRINT_USAGE(USAGE_FINALIZE_MULTISIG);
1128  return true;
1129  }
1130 
1131  try
1132  {
1133  if (!m_wallet->finalize_multisig(pwd_container->password(), args))
1134  {
1135  fail_msg_writer() << tr("Failed to finalize multisig");
1136  return true;
1137  }
1138  }
1139  catch (const std::exception &e)
1140  {
1141  fail_msg_writer() << tr("Failed to finalize multisig: ") << e.what();
1142  return true;
1143  }
1144 
1145  return true;
1146 }
1147 
1148 bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args)
1149 {
1150  exchange_multisig_keys_main(args, false);
1151  return true;
1152 }
1153 
1154 bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &args, bool called_by_mms) {
1155  bool ready;
1156  if (m_wallet->key_on_device())
1157  {
1158  fail_msg_writer() << tr("command not supported by HW wallet");
1159  return false;
1160  }
1161  if (!m_wallet->multisig(&ready))
1162  {
1163  fail_msg_writer() << tr("This wallet is not multisig");
1164  return false;
1165  }
1166  if (ready)
1167  {
1168  fail_msg_writer() << tr("This wallet is already finalized");
1169  return false;
1170  }
1171 
1172  const auto orig_pwd_container = get_and_verify_password();
1173  if(orig_pwd_container == boost::none)
1174  {
1175  fail_msg_writer() << tr("Your original password was incorrect.");
1176  return false;
1177  }
1178 
1179  if (args.size() < 2)
1180  {
1181  PRINT_USAGE(USAGE_EXCHANGE_MULTISIG_KEYS);
1182  return false;
1183  }
1184 
1185  try
1186  {
1187  std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
1188  if (!multisig_extra_info.empty())
1189  {
1190  message_writer() << tr("Another step is needed");
1191  message_writer() << multisig_extra_info;
1192  message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
1193  if (called_by_mms)
1194  {
1195  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::additional_key_set, multisig_extra_info);
1196  }
1197  return true;
1198  } else {
1199  uint32_t threshold, total;
1200  m_wallet->multisig(NULL, &threshold, &total);
1201  success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << threshold << "/" << total;
1202  success_msg_writer() << tr("Multisig address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
1203  }
1204  }
1205  catch (const std::exception &e)
1206  {
1207  fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what();
1208  return false;
1209  }
1210 
1211  return true;
1212 }
1213 
1214 bool simple_wallet::export_multisig(const std::vector<std::string> &args)
1215 {
1216  export_multisig_main(args, false);
1217  return true;
1218 }
1219 
1220 bool simple_wallet::export_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
1221 {
1222  bool ready;
1223  if (m_wallet->key_on_device())
1224  {
1225  fail_msg_writer() << tr("command not supported by HW wallet");
1226  return false;
1227  }
1228  if (!m_wallet->multisig(&ready))
1229  {
1230  fail_msg_writer() << tr("This wallet is not multisig");
1231  return false;
1232  }
1233  if (!ready)
1234  {
1235  fail_msg_writer() << tr("This multisig wallet is not yet finalized");
1236  return false;
1237  }
1238  if (args.size() != 1)
1239  {
1240  PRINT_USAGE(USAGE_EXPORT_MULTISIG_INFO);
1241  return false;
1242  }
1243 
1244  const std::string filename = args[0];
1245  if (!called_by_mms && m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
1246  return true;
1247 
1248  SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
1249 
1250  try
1251  {
1252  cryptonote::blobdata ciphertext = m_wallet->export_multisig();
1253 
1254  if (called_by_mms)
1255  {
1256  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::multisig_sync_data, ciphertext);
1257  }
1258  else
1259  {
1260  bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext);
1261  if (!r)
1262  {
1263  fail_msg_writer() << tr("failed to save file ") << filename;
1264  return false;
1265  }
1266  }
1267  }
1268  catch (const std::exception &e)
1269  {
1270  LOG_ERROR("Error exporting multisig info: " << e.what());
1271  fail_msg_writer() << tr("Error exporting multisig info: ") << e.what();
1272  return false;
1273  }
1274 
1275  success_msg_writer() << tr("Multisig info exported to ") << filename;
1276  return true;
1277 }
1278 
1279 bool simple_wallet::import_multisig(const std::vector<std::string> &args)
1280 {
1281  import_multisig_main(args, false);
1282  return true;
1283 }
1284 
1285 bool simple_wallet::import_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
1286 {
1287  bool ready;
1288  uint32_t threshold, total;
1289  if (m_wallet->key_on_device())
1290  {
1291  fail_msg_writer() << tr("command not supported by HW wallet");
1292  return false;
1293  }
1294  if (!m_wallet->multisig(&ready, &threshold, &total))
1295  {
1296  fail_msg_writer() << tr("This wallet is not multisig");
1297  return false;
1298  }
1299  if (!ready)
1300  {
1301  fail_msg_writer() << tr("This multisig wallet is not yet finalized");
1302  return false;
1303  }
1304  if (args.size() < threshold - 1)
1305  {
1306  PRINT_USAGE(USAGE_IMPORT_MULTISIG_INFO);
1307  return false;
1308  }
1309 
1310  std::vector<cryptonote::blobdata> info;
1311  for (size_t n = 0; n < args.size(); ++n)
1312  {
1313  if (called_by_mms)
1314  {
1315  info.push_back(args[n]);
1316  }
1317  else
1318  {
1319  const std::string &filename = args[n];
1320  std::string data;
1321  bool r = epee::file_io_utils::load_file_to_string(filename, data);
1322  if (!r)
1323  {
1324  fail_msg_writer() << tr("failed to read file ") << filename;
1325  return false;
1326  }
1327  info.push_back(std::move(data));
1328  }
1329  }
1330 
1331  SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
1332 
1333  // all read and parsed, actually import
1334  try
1335  {
1336  m_in_manual_refresh.store(true, std::memory_order_relaxed);
1337  epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
1338  size_t n_outputs = m_wallet->import_multisig(info);
1339  // Clear line "Height xxx of xxx"
1340  std::cout << "\r \r";
1341  success_msg_writer() << tr("Multisig info imported");
1342  }
1343  catch (const std::exception &e)
1344  {
1345  fail_msg_writer() << tr("Failed to import multisig info: ") << e.what();
1346  return false;
1347  }
1348  if (m_wallet->is_trusted_daemon())
1349  {
1350  try
1351  {
1352  m_wallet->rescan_spent();
1353  }
1354  catch (const std::exception &e)
1355  {
1356  message_writer() << tr("Failed to update spent status after importing multisig info: ") << e.what();
1357  return false;
1358  }
1359  }
1360  else
1361  {
1362  message_writer() << tr("Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run \"rescan_spent\"");
1363  return false;
1364  }
1365  return true;
1366 }
1367 
1368 bool simple_wallet::accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs)
1369 {
1370  std::string extra_message;
1371  return accept_loaded_tx([&txs](){return txs.m_ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.m_ptx[n].construction_data;}, extra_message);
1372 }
1373 
1374 bool simple_wallet::sign_multisig(const std::vector<std::string> &args)
1375 {
1376  sign_multisig_main(args, false);
1377  return true;
1378 }
1379 
1380 bool simple_wallet::sign_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
1381 {
1382  bool ready;
1383  if (m_wallet->key_on_device())
1384  {
1385  fail_msg_writer() << tr("command not supported by HW wallet");
1386  return false;
1387  }
1388  if(!m_wallet->multisig(&ready))
1389  {
1390  fail_msg_writer() << tr("This is not a multisig wallet");
1391  return false;
1392  }
1393  if (!ready)
1394  {
1395  fail_msg_writer() << tr("This multisig wallet is not yet finalized");
1396  return false;
1397  }
1398  if (args.size() != 1)
1399  {
1400  PRINT_USAGE(USAGE_SIGN_MULTISIG);
1401  return false;
1402  }
1403 
1404  SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
1405 
1406  std::string filename = args[0];
1407  std::vector<crypto::hash> txids;
1408  uint32_t signers = 0;
1409  try
1410  {
1411  if (called_by_mms)
1412  {
1413  tools::wallet2::multisig_tx_set exported_txs;
1414  std::string ciphertext;
1415  bool r = m_wallet->load_multisig_tx(args[0], exported_txs, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); });
1416  if (r)
1417  {
1418  r = m_wallet->sign_multisig_tx(exported_txs, txids);
1419  }
1420  if (r)
1421  {
1422  ciphertext = m_wallet->save_multisig_tx(exported_txs);
1423  if (ciphertext.empty())
1424  {
1425  r = false;
1426  }
1427  }
1428  if (r)
1429  {
1431  if (txids.empty())
1432  {
1434  }
1435  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), message_type, ciphertext);
1436  filename = "MMS"; // for the messages below
1437  }
1438  else
1439  {
1440  fail_msg_writer() << tr("Failed to sign multisig transaction");
1441  return false;
1442  }
1443  }
1444  else
1445  {
1446  bool r = m_wallet->sign_multisig_tx_from_file(filename, txids, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); });
1447  if (!r)
1448  {
1449  fail_msg_writer() << tr("Failed to sign multisig transaction");
1450  return false;
1451  }
1452  }
1453  }
1454  catch (const tools::error::multisig_export_needed& e)
1455  {
1456  fail_msg_writer() << tr("Multisig error: ") << e.what();
1457  return false;
1458  }
1459  catch (const std::exception &e)
1460  {
1461  fail_msg_writer() << tr("Failed to sign multisig transaction: ") << e.what();
1462  return false;
1463  }
1464 
1465  if (txids.empty())
1466  {
1468  m_wallet->multisig(NULL, &threshold);
1469  uint32_t signers_needed = threshold - signers - 1;
1470  success_msg_writer(true) << tr("Transaction successfully signed to file ") << filename << ", "
1471  << signers_needed << " more signer(s) needed";
1472  return true;
1473  }
1474  else
1475  {
1476  std::string txids_as_text;
1477  for (const auto &txid: txids)
1478  {
1479  if (!txids_as_text.empty())
1480  txids_as_text += (", ");
1481  txids_as_text += epee::string_tools::pod_to_hex(txid);
1482  }
1483  success_msg_writer(true) << tr("Transaction successfully signed to file ") << filename << ", txid " << txids_as_text;
1484  success_msg_writer(true) << tr("It may be relayed to the network with submit_multisig");
1485  }
1486  return true;
1487 }
1488 
1489 bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
1490 {
1491  submit_multisig_main(args, false);
1492  return true;
1493 }
1494 
1495 bool simple_wallet::submit_multisig_main(const std::vector<std::string> &args, bool called_by_mms)
1496 {
1497  bool ready;
1499  if (m_wallet->key_on_device())
1500  {
1501  fail_msg_writer() << tr("command not supported by HW wallet");
1502  return false;
1503  }
1504  if (!m_wallet->multisig(&ready, &threshold))
1505  {
1506  fail_msg_writer() << tr("This is not a multisig wallet");
1507  return false;
1508  }
1509  if (!ready)
1510  {
1511  fail_msg_writer() << tr("This multisig wallet is not yet finalized");
1512  return false;
1513  }
1514  if (args.size() != 1)
1515  {
1516  PRINT_USAGE(USAGE_SUBMIT_MULTISIG);
1517  return false;
1518  }
1519 
1520  if (!try_connect_to_daemon())
1521  return false;
1522 
1523  SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
1524 
1525  std::string filename = args[0];
1526  try
1527  {
1529  if (called_by_mms)
1530  {
1531  bool r = m_wallet->load_multisig_tx(args[0], txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); });
1532  if (!r)
1533  {
1534  fail_msg_writer() << tr("Failed to load multisig transaction from MMS");
1535  return false;
1536  }
1537  }
1538  else
1539  {
1540  bool r = m_wallet->load_multisig_tx_from_file(filename, txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); });
1541  if (!r)
1542  {
1543  fail_msg_writer() << tr("Failed to load multisig transaction from file");
1544  return false;
1545  }
1546  }
1547  if (txs.m_signers.size() < threshold)
1548  {
1549  fail_msg_writer() << (boost::format(tr("Multisig transaction signed by only %u signers, needs %u more signatures"))
1550  % txs.m_signers.size() % (threshold - txs.m_signers.size())).str();
1551  return false;
1552  }
1553 
1554  // actually commit the transactions
1555  for (auto &ptx: txs.m_ptx)
1556  {
1557  m_wallet->commit_tx(ptx);
1558  success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << get_transaction_hash(ptx.tx) << ENDL
1559  << tr("You can check its status by using the `show_transfers` command.");
1560  }
1561  }
1562  catch (const std::exception &e)
1563  {
1564  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
1565  }
1566  catch (...)
1567  {
1568  LOG_ERROR("unknown error");
1569  fail_msg_writer() << tr("unknown error");
1570  return false;
1571  }
1572 
1573  return true;
1574 }
1575 
1576 bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args)
1577 {
1578  bool ready;
1580  if (m_wallet->key_on_device())
1581  {
1582  fail_msg_writer() << tr("command not supported by HW wallet");
1583  return true;
1584  }
1585  if (!m_wallet->multisig(&ready, &threshold))
1586  {
1587  fail_msg_writer() << tr("This is not a multisig wallet");
1588  return true;
1589  }
1590  if (!ready)
1591  {
1592  fail_msg_writer() << tr("This multisig wallet is not yet finalized");
1593  return true;
1594  }
1595  if (args.size() != 1)
1596  {
1597  PRINT_USAGE(USAGE_EXPORT_RAW_MULTISIG_TX);
1598  return true;
1599  }
1600 
1601  std::string filename = args[0];
1602  if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
1603  return true;
1604 
1606 
1607  try
1608  {
1610  bool r = m_wallet->load_multisig_tx_from_file(filename, txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); });
1611  if (!r)
1612  {
1613  fail_msg_writer() << tr("Failed to load multisig transaction from file");
1614  return true;
1615  }
1616  if (txs.m_signers.size() < threshold)
1617  {
1618  fail_msg_writer() << (boost::format(tr("Multisig transaction signed by only %u signers, needs %u more signatures"))
1619  % txs.m_signers.size() % (threshold - txs.m_signers.size())).str();
1620  return true;
1621  }
1622 
1623  // save the transactions
1624  std::string filenames;
1625  for (auto &ptx: txs.m_ptx)
1626  {
1627  const crypto::hash txid = cryptonote::get_transaction_hash(ptx.tx);
1628  const std::string filename = std::string("raw_multisig_etn_tx_") + epee::string_tools::pod_to_hex(txid);
1629  if (!filenames.empty())
1630  filenames += ", ";
1631  filenames += filename;
1633  {
1634  fail_msg_writer() << tr("Failed to export multisig transaction to file ") << filename;
1635  return true;
1636  }
1637  }
1638  success_msg_writer() << tr("Saved exported multisig transaction file(s): ") << filenames;
1639  }
1640  catch (const std::exception& e)
1641  {
1642  LOG_ERROR("unexpected error: " << e.what());
1643  fail_msg_writer() << tr("unexpected error: ") << e.what();
1644  }
1645  catch (...)
1646  {
1647  LOG_ERROR("Unknown error");
1648  fail_msg_writer() << tr("unknown error");
1649  }
1650 
1651  return true;
1652 }
1653 
1654 bool simple_wallet::print_ring(const std::vector<std::string> &args)
1655 {
1657  crypto::hash txid;
1658  if (args.size() != 1)
1659  {
1660  PRINT_USAGE(USAGE_PRINT_RING);
1661  return true;
1662  }
1663 
1665  {
1666  fail_msg_writer() << tr("Invalid key image");
1667  return true;
1668  }
1669  // this one will always work, they're all 32 byte hex
1670  if (!epee::string_tools::hex_to_pod(args[0], txid))
1671  {
1672  fail_msg_writer() << tr("Invalid txid");
1673  return true;
1674  }
1675 
1676  std::vector<uint64_t> ring;
1677  std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings;
1678  try
1679  {
1680  if (m_wallet->get_ring(key_image, ring))
1681  rings.push_back({key_image, ring});
1682  else if (!m_wallet->get_rings(txid, rings))
1683  {
1684  fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0");
1685  return true;
1686  }
1687 
1688  for (const auto &ring: rings)
1689  {
1690  std::stringstream str;
1691  for (const auto &x: ring.second)
1692  str << x<< " ";
1693  // do NOT translate this "absolute" below, the lin can be used as input to set_ring
1694  success_msg_writer() << epee::string_tools::pod_to_hex(ring.first) << " absolute " << str.str();
1695  }
1696  }
1697  catch (const std::exception &e)
1698  {
1699  fail_msg_writer() << tr("Failed to get key image ring: ") << e.what();
1700  }
1701 
1702  return true;
1703 }
1704 
1705 bool simple_wallet::set_ring(const std::vector<std::string> &args)
1706 {
1708 
1709  // try filename first
1710  if (args.size() == 1)
1711  {
1712  if (!epee::file_io_utils::is_file_exist(args[0]))
1713  {
1714  fail_msg_writer() << tr("File doesn't exist");
1715  return true;
1716  }
1717 
1718  char str[4096];
1719  std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r"));
1720  if (f)
1721  {
1722  while (!feof(f.get()))
1723  {
1724  if (!fgets(str, sizeof(str), f.get()))
1725  break;
1726  const size_t len = strlen(str);
1727  if (len > 0 && str[len - 1] == '\n')
1728  str[len - 1] = 0;
1729  if (!str[0])
1730  continue;
1731  char key_image_str[65], type_str[9];
1732  int read_after_key_image = 0, read = 0;
1733  int fields = sscanf(str, "%64[abcdefABCDEF0123456789] %n%8s %n", key_image_str, &read_after_key_image, type_str, &read);
1734  if (fields != 2)
1735  {
1736  fail_msg_writer() << tr("Invalid ring specification: ") << str;
1737  continue;
1738  }
1739  key_image_str[64] = 0;
1740  type_str[8] = 0;
1742  if (read_after_key_image == 0 || !epee::string_tools::hex_to_pod(key_image_str, key_image))
1743  {
1744  fail_msg_writer() << tr("Invalid key image: ") << str;
1745  continue;
1746  }
1747  if (read == read_after_key_image+8 || (strcmp(type_str, "absolute") && strcmp(type_str, "relative")))
1748  {
1749  fail_msg_writer() << tr("Invalid ring type, expected relative or abosolute: ") << str;
1750  continue;
1751  }
1752  bool relative = !strcmp(type_str, "relative");
1753  if (read < 0 || (size_t)read > strlen(str))
1754  {
1755  fail_msg_writer() << tr("Error reading line: ") << str;
1756  continue;
1757  }
1758  bool valid = true;
1759  std::vector<uint64_t> ring;
1760  const char *ptr = str + read;
1761  while (*ptr)
1762  {
1763  unsigned long offset;
1764  int elements = sscanf(ptr, "%lu %n", &offset, &read);
1765  if (elements == 0 || read <= 0 || (size_t)read > strlen(str))
1766  {
1767  fail_msg_writer() << tr("Error reading line: ") << str;
1768  valid = false;
1769  break;
1770  }
1771  ring.push_back(offset);
1772  ptr += read;
1773  }
1774  if (!valid)
1775  continue;
1776  if (ring.empty())
1777  {
1778  fail_msg_writer() << tr("Invalid ring: ") << str;
1779  continue;
1780  }
1781  if (relative)
1782  {
1783  for (size_t n = 1; n < ring.size(); ++n)
1784  {
1785  if (ring[n] <= 0)
1786  {
1787  fail_msg_writer() << tr("Invalid relative ring: ") << str;
1788  valid = false;
1789  break;
1790  }
1791  }
1792  }
1793  else
1794  {
1795  for (size_t n = 1; n < ring.size(); ++n)
1796  {
1797  if (ring[n] <= ring[n-1])
1798  {
1799  fail_msg_writer() << tr("Invalid absolute ring: ") << str;
1800  valid = false;
1801  break;
1802  }
1803  }
1804  }
1805  if (!valid)
1806  continue;
1807  if (!m_wallet->set_ring(key_image, ring, relative))
1808  fail_msg_writer() << tr("Failed to set ring for key image: ") << key_image << ". " << tr("Continuing.");
1809  }
1810  f.reset();
1811  }
1812  return true;
1813  }
1814 
1815  if (args.size() < 3)
1816  {
1817  PRINT_USAGE(USAGE_SET_RING);
1818  return true;
1819  }
1820 
1822  {
1823  fail_msg_writer() << tr("Invalid key image");
1824  return true;
1825  }
1826 
1827  bool relative;
1828  if (args[1] == "absolute")
1829  {
1830  relative = false;
1831  }
1832  else if (args[1] == "relative")
1833  {
1834  relative = true;
1835  }
1836  else
1837  {
1838  fail_msg_writer() << tr("Missing absolute or relative keyword");
1839  return true;
1840  }
1841 
1842  std::vector<uint64_t> ring;
1843  for (size_t n = 2; n < args.size(); ++n)
1844  {
1845  ring.resize(ring.size() + 1);
1846  if (!string_tools::get_xtype_from_string(ring.back(), args[n]))
1847  {
1848  fail_msg_writer() << tr("invalid index: must be a strictly positive unsigned integer");
1849  return true;
1850  }
1851  if (relative)
1852  {
1853  if (ring.size() > 1 && !ring.back())
1854  {
1855  fail_msg_writer() << tr("invalid index: must be a strictly positive unsigned integer");
1856  return true;
1857  }
1858  uint64_t sum = 0;
1859  for (uint64_t out: ring)
1860  {
1861  if (out > std::numeric_limits<uint64_t>::max() - sum)
1862  {
1863  fail_msg_writer() << tr("invalid index: indices wrap");
1864  return true;
1865  }
1866  sum += out;
1867  }
1868  }
1869  else
1870  {
1871  if (ring.size() > 1 && ring[ring.size() - 2] >= ring[ring.size() - 1])
1872  {
1873  fail_msg_writer() << tr("invalid index: indices should be in strictly ascending order");
1874  return true;
1875  }
1876  }
1877  }
1878  if (!m_wallet->set_ring(key_image, ring, relative))
1879  {
1880  fail_msg_writer() << tr("failed to set ring");
1881  return true;
1882  }
1883 
1884  return true;
1885 }
1886 
1887 bool simple_wallet::unset_ring(const std::vector<std::string> &args)
1888 {
1889  crypto::hash txid;
1890  std::vector<crypto::key_image> key_images;
1891 
1892  if (args.size() < 1)
1893  {
1894  PRINT_USAGE(USAGE_UNSET_RING);
1895  return true;
1896  }
1897 
1898  key_images.resize(args.size());
1899  for (size_t i = 0; i < args.size(); ++i)
1900  {
1901  if (!epee::string_tools::hex_to_pod(args[i], key_images[i]))
1902  {
1903  fail_msg_writer() << tr("Invalid key image or txid");
1904  return true;
1905  }
1906  }
1907  static_assert(sizeof(crypto::hash) == sizeof(crypto::key_image), "hash and key_image must have the same size");
1908  memcpy(&txid, &key_images[0], sizeof(txid));
1909 
1910  if (!m_wallet->unset_ring(key_images) && !m_wallet->unset_ring(txid))
1911  {
1912  fail_msg_writer() << tr("failed to unset ring");
1913  return true;
1914  }
1915 
1916  return true;
1917 }
1918 
1919 bool simple_wallet::blackball(const std::vector<std::string> &args)
1920 {
1921  uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
1922  if (args.size() == 0)
1923  {
1924  PRINT_USAGE(USAGE_MARK_OUTPUT_SPENT);
1925  return true;
1926  }
1927 
1928  try
1929  {
1930  if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &amount, &offset) == 2)
1931  {
1932  m_wallet->blackball_output(std::make_pair(amount, offset));
1933  }
1934  else if (epee::file_io_utils::is_file_exist(args[0]))
1935  {
1936  std::vector<std::pair<uint64_t, uint64_t>> outputs;
1937  char str[256];
1938 
1939  std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r"));
1940  if (f)
1941  {
1942  while (!feof(f.get()))
1943  {
1944  if (!fgets(str, sizeof(str), f.get()))
1945  break;
1946  const size_t len = strlen(str);
1947  if (len > 0 && str[len - 1] == '\n')
1948  str[len - 1] = 0;
1949  if (!str[0])
1950  continue;
1951  if (sscanf(str, "@%" PRIu64, &amount) == 1)
1952  {
1953  continue;
1954  }
1955  if (amount == std::numeric_limits<uint64_t>::max())
1956  {
1957  fail_msg_writer() << tr("First line is not an amount");
1958  return true;
1959  }
1960  if (sscanf(str, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits<uint64_t>::max() - offset)
1961  {
1962  while (num_offsets--)
1963  outputs.push_back(std::make_pair(amount, offset++));
1964  }
1965  else if (sscanf(str, "%" PRIu64, &offset) == 1)
1966  {
1967  outputs.push_back(std::make_pair(amount, offset));
1968  }
1969  else
1970  {
1971  fail_msg_writer() << tr("Invalid output: ") << str;
1972  return true;
1973  }
1974  }
1975  f.reset();
1976  bool add = false;
1977  if (args.size() > 1)
1978  {
1979  if (args[1] != "add")
1980  {
1981  fail_msg_writer() << tr("Bad argument: ") + args[1] + ": " + tr("should be \"add\"");
1982  return true;
1983  }
1984  add = true;
1985  }
1986  m_wallet->set_blackballed_outputs(outputs, add);
1987  }
1988  else
1989  {
1990  fail_msg_writer() << tr("Failed to open file");
1991  return true;
1992  }
1993  }
1994  else
1995  {
1996  fail_msg_writer() << tr("Invalid output key, and file doesn't exist");
1997  return true;
1998  }
1999  }
2000  catch (const std::exception &e)
2001  {
2002  fail_msg_writer() << tr("Failed to mark output spent: ") << e.what();
2003  }
2004 
2005  return true;
2006 }
2007 
2008 bool simple_wallet::unblackball(const std::vector<std::string> &args)
2009 {
2010  std::pair<uint64_t, uint64_t> output;
2011  if (args.size() != 1)
2012  {
2013  PRINT_USAGE(USAGE_MARK_OUTPUT_UNSPENT);
2014  return true;
2015  }
2016 
2017  if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2)
2018  {
2019  fail_msg_writer() << tr("Invalid output");
2020  return true;
2021  }
2022 
2023  try
2024  {
2025  m_wallet->unblackball_output(output);
2026  }
2027  catch (const std::exception &e)
2028  {
2029  fail_msg_writer() << tr("Failed to mark output unspent: ") << e.what();
2030  }
2031 
2032  return true;
2033 }
2034 
2035 bool simple_wallet::blackballed(const std::vector<std::string> &args)
2036 {
2037  std::pair<uint64_t, uint64_t> output;
2038  if (args.size() != 1)
2039  {
2040  PRINT_USAGE(USAGE_IS_OUTPUT_SPENT);
2041  return true;
2042  }
2043 
2044  if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2)
2045  {
2046  fail_msg_writer() << tr("Invalid output");
2047  return true;
2048  }
2049 
2050  try
2051  {
2052  if (m_wallet->is_output_blackballed(output))
2053  message_writer() << tr("Spent: ") << output.first << "/" << output.second;
2054  else
2055  message_writer() << tr("Not spent: ") << output.first << "/" << output.second;
2056  }
2057  catch (const std::exception &e)
2058  {
2059  fail_msg_writer() << tr("Failed to check whether output is spent: ") << e.what();
2060  }
2061 
2062  return true;
2063 }
2064 
2065 bool simple_wallet::save_known_rings(const std::vector<std::string> &args)
2066 {
2067  try
2068  {
2069  LOCK_IDLE_SCOPE();
2070  m_wallet->find_and_save_rings();
2071  }
2072  catch (const std::exception &e)
2073  {
2074  fail_msg_writer() << tr("Failed to save known rings: ") << e.what();
2075  }
2076  return true;
2077 }
2078 
2079 bool simple_wallet::freeze_thaw(const std::vector<std::string> &args, bool freeze)
2080 {
2081  if (args.empty())
2082  {
2083  fail_msg_writer() << boost::format(tr("usage: %s <key_image>|<pubkey>")) % (freeze ? "freeze" : "thaw");
2084  return true;
2085  }
2086  crypto::key_image ki;
2087  if (!epee::string_tools::hex_to_pod(args[0], ki))
2088  {
2089  fail_msg_writer() << tr("failed to parse key image");
2090  return true;
2091  }
2092  try
2093  {
2094  if (freeze)
2095  m_wallet->freeze(ki);
2096  else
2097  m_wallet->thaw(ki);
2098  }
2099  catch (const std::exception &e)
2100  {
2101  fail_msg_writer() << e.what();
2102  return true;
2103  }
2104 
2105  return true;
2106 }
2107 
2108 bool simple_wallet::freeze(const std::vector<std::string> &args)
2109 {
2110  return freeze_thaw(args, true);
2111 }
2112 
2113 bool simple_wallet::thaw(const std::vector<std::string> &args)
2114 {
2115  return freeze_thaw(args, false);
2116 }
2117 
2118 bool simple_wallet::frozen(const std::vector<std::string> &args)
2119 {
2120  if (args.empty())
2121  {
2122  size_t ntd = m_wallet->get_num_transfer_details();
2123  for (size_t i = 0; i < ntd; ++i)
2124  {
2125  if (!m_wallet->frozen(i))
2126  continue;
2127  const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
2128  message_writer() << tr("Frozen: ") << td.m_key_image << " " << cryptonote::print_etn(td.amount());
2129  }
2130  }
2131  else
2132  {
2133  crypto::key_image ki;
2134  if (!epee::string_tools::hex_to_pod(args[0], ki))
2135  {
2136  fail_msg_writer() << tr("failed to parse key image");
2137  return true;
2138  }
2139  if (m_wallet->frozen(ki))
2140  message_writer() << tr("Frozen: ") << ki;
2141  else
2142  message_writer() << tr("Not frozen: ") << ki;
2143  }
2144  return true;
2145 }
2146 
2147 bool simple_wallet::net_stats(const std::vector<std::string> &args)
2148 {
2149  message_writer() << std::to_string(m_wallet->get_bytes_sent()) + tr(" bytes sent");
2150  message_writer() << std::to_string(m_wallet->get_bytes_received()) + tr(" bytes received");
2151  return true;
2152 }
2153 
2154 bool simple_wallet::welcome(const std::vector<std::string> &args)
2155 {
2156  message_writer() << tr("Welcome to Electroneum; the global, humanitarian cryptocurrency for the masses.");
2157  message_writer() << "";
2158  message_writer() << tr("For more information, see https://electroneum.com/");
2159  return true;
2160 }
2161 
2162 bool simple_wallet::version(const std::vector<std::string> &args)
2163 {
2164  message_writer() << "Electroneum '" << ELECTRONEUM_RELEASE_NAME << "' (v" << ELECTRONEUM_VERSION_FULL << ")";
2165  return true;
2166 }
2167 
2168 bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func)
2169 {
2170  std::vector<std::string> tx_aux;
2171 
2172  message_writer(console_color_white, false) << tr("Please confirm the transaction on the device");
2173 
2174  m_wallet->cold_sign_tx(ptx_vector, exported_txs, dsts_info, tx_aux);
2175 
2176  if (accept_func && !accept_func(exported_txs))
2177  {
2178  MERROR("Transactions rejected by callback");
2179  return false;
2180  }
2181 
2182  // aux info
2183  m_wallet->cold_tx_aux_import(exported_txs.ptx, tx_aux);
2184 
2185  // import key images
2186  return m_wallet->import_key_images(exported_txs, 0, true);
2187 }
2188 
2189 bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2190 {
2191  const auto pwd_container = get_and_verify_password();
2192  if (pwd_container)
2193  {
2194  parse_bool_and_use(args[1], [&](bool r) {
2195  m_wallet->always_confirm_transfers(r);
2196  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2197  });
2198  }
2199  return true;
2200 }
2201 
2202 bool simple_wallet::set_print_ring_members(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2203 {
2204  const auto pwd_container = get_and_verify_password();
2205  if (pwd_container)
2206  {
2207  parse_bool_and_use(args[1], [&](bool r) {
2208  m_wallet->print_ring_members(r);
2209  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2210  });
2211  }
2212  return true;
2213 }
2214 
2215 bool simple_wallet::set_store_tx_info(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2216 {
2217  if (m_wallet->watch_only())
2218  {
2219  fail_msg_writer() << tr("wallet is watch-only and cannot transfer");
2220  return true;
2221  }
2222 
2223  const auto pwd_container = get_and_verify_password();
2224  if (pwd_container)
2225  {
2226  parse_bool_and_use(args[1], [&](bool r) {
2227  m_wallet->store_tx_info(r);
2228  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2229  });
2230  }
2231  return true;
2232 }
2233 
2234 bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2235 {
2236  if (m_wallet->watch_only())
2237  {
2238  fail_msg_writer() << tr("wallet is watch-only and cannot transfer");
2239  return true;
2240  }
2241 
2242  if(m_wallet->use_fork_rules(HF_VERSION_ENFORCE_0_DECOY_TXS, 0) && !m_wallet->use_fork_rules(HF_VERSION_ENFORCE_0_DECOY_TXS_END, 0)) {
2243  fail_msg_writer() << tr("Cannot set default ring size: ring size is enforced at 1.");
2244  return true;
2245  }
2246 
2247  try
2248  {
2249  if (strchr(args[1].c_str(), '-'))
2250  {
2251  fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE;
2252  return true;
2253  }
2254  uint32_t ring_size = boost::lexical_cast<uint32_t>(args[1]);
2255  if (ring_size < MIN_RING_SIZE && ring_size != 0)
2256  {
2257  fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE;
2258  return true;
2259  }
2260 
2261  if (ring_size != 0 && ring_size != DEFAULT_MIX+1)
2262  message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
2263  else if (ring_size == DEFAULT_MIX)
2264  message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored.");
2265 
2266  const auto pwd_container = get_and_verify_password();
2267  if (pwd_container)
2268  {
2269  m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0);
2270  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2271  }
2272  return true;
2273  }
2274  catch(const boost::bad_lexical_cast &)
2275  {
2276  fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE;
2277  return true;
2278  }
2279  catch(...)
2280  {
2281  fail_msg_writer() << tr("could not change default ring size");
2282  return true;
2283  }
2284 }
2285 
2286 bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2287 {
2288  uint32_t priority = 0;
2289  try
2290  {
2291  if (strchr(args[1].c_str(), '-'))
2292  {
2293  fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
2294  return true;
2295  }
2296  if (args[1] == "0")
2297  {
2298  priority = 0;
2299  }
2300  else
2301  {
2302  bool found = false;
2303  for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
2304  {
2305  if (allowed_priority_strings[n] == args[1])
2306  {
2307  found = true;
2308  priority = n;
2309  }
2310  }
2311  if (!found)
2312  {
2313  priority = boost::lexical_cast<int>(args[1]);
2314  if (priority < 1 || priority > 4)
2315  {
2316  fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
2317  return true;
2318  }
2319  }
2320  }
2321 
2322  const auto pwd_container = get_and_verify_password();
2323  if (pwd_container)
2324  {
2325  m_wallet->set_default_priority(priority);
2326  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2327  }
2328  return true;
2329  }
2330  catch(const boost::bad_lexical_cast &)
2331  {
2332  fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
2333  return true;
2334  }
2335  catch(...)
2336  {
2337  fail_msg_writer() << tr("could not change default priority");
2338  return true;
2339  }
2340 }
2341 
2342 bool simple_wallet::set_auto_refresh(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2343 {
2344  const auto pwd_container = get_and_verify_password();
2345  if (pwd_container)
2346  {
2347  parse_bool_and_use(args[1], [&](bool auto_refresh) {
2348  m_auto_refresh_enabled.store(false, std::memory_order_relaxed);
2349  m_wallet->auto_refresh(auto_refresh);
2350  m_idle_mutex.lock();
2351  m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed);
2352  m_idle_cond.notify_one();
2353  m_idle_mutex.unlock();
2354 
2355  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2356  });
2357  }
2358  return true;
2359 }
2360 
2361 bool simple_wallet::set_refresh_type(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2362 {
2363  tools::wallet2::RefreshType refresh_type;
2364  if (!parse_refresh_type(args[1], refresh_type))
2365  {
2366  return true;
2367  }
2368 
2369  const auto pwd_container = get_and_verify_password();
2370  if (pwd_container)
2371  {
2372  m_wallet->set_refresh_type(refresh_type);
2373  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2374  }
2375  return true;
2376 }
2377 
2378 bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2379 {
2381 
2382  const auto pwd_container = get_and_verify_password();
2383  if (pwd_container)
2384  {
2385  parse_bool_and_use(args[1], [&](bool r) {
2386  m_wallet->confirm_missing_payment_id(r);
2387  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2388  });
2389  }
2390  return true;
2391 }
2392 
2393 bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2394 {
2395  const auto pwd_container = get_and_verify_password();
2396  if (pwd_container)
2397  {
2399  if (args[1] == "never" || args[1] == "0")
2401  else if (args[1] == "action" || args[1] == "1")
2403  else if (args[1] == "encrypt" || args[1] == "decrypt" || args[1] == "2")
2405  else
2406  {
2407  fail_msg_writer() << tr("invalid argument: must be either 0/never, 1/action, or 2/encrypt/decrypt");
2408  return true;
2409  }
2410 
2411  const tools::wallet2::AskPasswordType cur_ask = m_wallet->ask_password();
2412  if (!m_wallet->watch_only())
2413  {
2415  m_wallet->decrypt_keys(pwd_container->password());
2417  m_wallet->encrypt_keys(pwd_container->password());
2418  }
2419  m_wallet->ask_password(ask);
2420  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2421  }
2422  return true;
2423 }
2424 
2425 bool simple_wallet::set_unit(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2426 {
2427  const std::string &unit = args[1];
2428  unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT;
2429 
2430  if (unit == "electroneum")
2431  decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT;
2432  else if (unit == "ecent")
2433  decimal_point = 0;
2434  else
2435  {
2436  fail_msg_writer() << tr("invalid unit");
2437  return true;
2438  }
2439 
2440  const auto pwd_container = get_and_verify_password();
2441  if (pwd_container)
2442  {
2444  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2445  }
2446  return true;
2447 }
2448 
2449 bool simple_wallet::set_min_output_count(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2450 {
2451  uint32_t count;
2453  {
2454  fail_msg_writer() << tr("invalid count: must be an unsigned integer");
2455  return true;
2456  }
2457 
2458  const auto pwd_container = get_and_verify_password();
2459  if (pwd_container)
2460  {
2461  m_wallet->set_min_output_count(count);
2462  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2463  }
2464  return true;
2465 }
2466 
2467 bool simple_wallet::set_min_output_value(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2468 {
2469  uint64_t value;
2470  if (!cryptonote::parse_amount(value, args[1]))
2471  {
2472  fail_msg_writer() << tr("invalid value");
2473  return true;
2474  }
2475 
2476  const auto pwd_container = get_and_verify_password();
2477  if (pwd_container)
2478  {
2479  m_wallet->set_min_output_value(value);
2480  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2481  }
2482  return true;
2483 }
2484 
2485 bool simple_wallet::set_merge_destinations(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2486 {
2487  const auto pwd_container = get_and_verify_password();
2488  if (pwd_container)
2489  {
2490  parse_bool_and_use(args[1], [&](bool r) {
2491  m_wallet->merge_destinations(r);
2492  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2493  });
2494  }
2495  return true;
2496 }
2497 
2498 bool simple_wallet::set_confirm_backlog(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2499 {
2500  const auto pwd_container = get_and_verify_password();
2501  if (pwd_container)
2502  {
2503  parse_bool_and_use(args[1], [&](bool r) {
2504  m_wallet->confirm_backlog(r);
2505  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2506  });
2507  }
2508  return true;
2509 }
2510 
2511 bool simple_wallet::set_confirm_backlog_threshold(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2512 {
2515  {
2516  fail_msg_writer() << tr("invalid count: must be an unsigned integer");
2517  return true;
2518  }
2519 
2520  const auto pwd_container = get_and_verify_password();
2521  if (pwd_container)
2522  {
2523  m_wallet->set_confirm_backlog_threshold(threshold);
2524  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2525  }
2526  return true;
2527 }
2528 
2529 bool simple_wallet::set_confirm_export_overwrite(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2530 {
2531  const auto pwd_container = get_and_verify_password();
2532  if (pwd_container)
2533  {
2534  parse_bool_and_use(args[1], [&](bool r) {
2535  m_wallet->confirm_export_overwrite(r);
2536  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2537  });
2538  }
2539  return true;
2540 }
2541 
2542 bool simple_wallet::set_refresh_from_block_height(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2543 {
2544  const auto pwd_container = get_and_verify_password();
2545  if (pwd_container)
2546  {
2547  uint64_t height;
2549  {
2550  fail_msg_writer() << tr("Invalid height");
2551  return true;
2552  }
2553  m_wallet->set_refresh_from_block_height(height);
2554  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2555  }
2556  return true;
2557 }
2558 
2559 bool simple_wallet::set_account_major_offset(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2560 {
2561  const auto pwd_container = get_and_verify_password();
2562  if (pwd_container)
2563  {
2564  uint64_t offset;
2565  if (!epee::string_tools::get_xtype_from_string(offset, args[1]))
2566  {
2567  fail_msg_writer() << tr("Invalid offset");
2568  return true;
2569  }
2570  m_wallet->account_major_offset(offset);
2571  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2572  }
2573  return true;
2574 }
2575 
2576 bool simple_wallet::set_auto_low_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2577 {
2578  const auto pwd_container = get_and_verify_password();
2579  if (pwd_container)
2580  {
2581  parse_bool_and_use(args[1], [&](bool r) {
2582  m_wallet->auto_low_priority(r);
2583  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2584  });
2585  }
2586  return true;
2587 }
2588 
2589 bool simple_wallet::set_segregate_pre_fork_outputs(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2590 {
2591  const auto pwd_container = get_and_verify_password();
2592  if (pwd_container)
2593  {
2594  parse_bool_and_use(args[1], [&](bool r) {
2595  m_wallet->segregate_pre_fork_outputs(r);
2596  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2597  });
2598  }
2599  return true;
2600 }
2601 
2602 bool simple_wallet::set_key_reuse_mitigation2(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2603 {
2604  const auto pwd_container = get_and_verify_password();
2605  if (pwd_container)
2606  {
2607  parse_bool_and_use(args[1], [&](bool r) {
2608  m_wallet->key_reuse_mitigation2(r);
2609  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2610  });
2611  }
2612  return true;
2613 }
2614 
2615 bool simple_wallet::set_subaddress_lookahead(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2616 {
2617  const auto pwd_container = get_and_verify_password();
2618  if (pwd_container)
2619  {
2620  auto lookahead = parse_subaddress_lookahead(args[1]);
2621  if (lookahead)
2622  {
2623  m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
2624  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2625  }
2626  }
2627  return true;
2628 }
2629 
2630 bool simple_wallet::set_segregation_height(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2631 {
2632  const auto pwd_container = get_and_verify_password();
2633  if (pwd_container)
2634  {
2635  uint64_t height;
2637  {
2638  fail_msg_writer() << tr("Invalid height");
2639  return true;
2640  }
2641  m_wallet->segregation_height(height);
2642  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2643  }
2644  return true;
2645 }
2646 
2647 bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2648 {
2649  const auto pwd_container = get_and_verify_password();
2650  if (pwd_container)
2651  {
2652  parse_bool_and_use(args[1], [&](bool r) {
2653  m_wallet->ignore_fractional_outputs(r);
2654  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2655  });
2656  }
2657  return true;
2658 }
2659 
2660 bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2661 {
2662  const auto pwd_container = get_and_verify_password();
2663  if (pwd_container)
2664  {
2665  parse_bool_and_use(args[1], [&](bool r) {
2666  m_wallet->track_uses(r);
2667  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2668  });
2669  }
2670  return true;
2671 }
2672 
2673 bool simple_wallet::set_setup_background_mining(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2674 {
2675  const auto pwd_container = get_and_verify_password();
2676  if (pwd_container)
2677  {
2679  if (args[1] == "yes" || args[1] == "1")
2681  else if (args[1] == "no" || args[1] == "0")
2683  else
2684  {
2685  fail_msg_writer() << tr("invalid argument: must be either 1/yes or 0/no");
2686  return true;
2687  }
2688  m_wallet->setup_background_mining(setup);
2689  m_wallet->rewrite(m_wallet_file, pwd_container->password());
2691  start_background_mining();
2692  else
2693  stop_background_mining();
2694  }
2695  return true;
2696 }
2697 
2698 bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2699 {
2700  const auto pwd_container = get_and_verify_password();
2701  if (pwd_container)
2702  {
2703  if (args.size() == 0){
2704  fail_msg_writer() << tr("Device name not specified");
2705  return true;
2706  }
2707 
2708  m_wallet->device_name(args[0]);
2709  bool r = false;
2710  try {
2711  r = m_wallet->reconnect_device();
2712  if (!r){
2713  fail_msg_writer() << tr("Device reconnect failed");
2714  }
2715 
2716  } catch(const std::exception & e){
2717  MWARNING("Device reconnect failed: " << e.what());
2718  fail_msg_writer() << tr("Device reconnect failed: ") << e.what();
2719  }
2720 
2721  }
2722  return true;
2723 }
2724 
2725 bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
2726 {
2727  if(args.empty())
2728  {
2729  success_msg_writer() << get_commands_str();
2730  }
2731  else if ((args.size() == 2) && (args.front() == "mms"))
2732  {
2733  // Little hack to be able to do "help mms <subcommand>"
2734  std::vector<std::string> mms_args(1, args.front() + " " + args.back());
2735  success_msg_writer() << get_command_usage(mms_args);
2736  }
2737  else
2738  {
2739  success_msg_writer() << get_command_usage(args);
2740  }
2741  return true;
2742 }
2743 
2744 simple_wallet::simple_wallet()
2745  : m_allow_mismatched_daemon_version(false)
2746  , m_refresh_progress_reporter(*this)
2747  , m_idle_run(true)
2748  , m_auto_refresh_enabled(false)
2749  , m_auto_refresh_refreshing(false)
2750  , m_in_manual_refresh(false)
2751  , m_current_subaddress_account(0)
2752 {
2753  /*
2754  m_cmd_binder.set_handler("start_mining",
2755  boost::bind(&simple_wallet::start_mining, this, _1),
2756  tr(USAGE_START_MINING),
2757  tr("Start mining in the daemon (bg_mining and ignore_battery are optional booleans)."));
2758  m_cmd_binder.set_handler("stop_mining",
2759  boost::bind(&simple_wallet::stop_mining, this, _1),
2760  tr("Stop mining in the daemon."));
2761  */
2762  m_cmd_binder.set_handler("set_daemon",
2763  boost::bind(&simple_wallet::set_daemon, this, _1),
2764  tr(USAGE_SET_DAEMON),
2765  tr("Set another daemon to connect to."));
2766  m_cmd_binder.set_handler("save_bc",
2767  boost::bind(&simple_wallet::save_bc, this, _1),
2768  tr("Save the current blockchain data."));
2769  m_cmd_binder.set_handler("refresh",
2770  boost::bind(&simple_wallet::refresh, this, _1),
2771  tr("Synchronize the transactions and balance."));
2772  m_cmd_binder.set_handler("balance",
2773  boost::bind(&simple_wallet::show_balance, this, _1),
2774  tr(USAGE_SHOW_BALANCE),
2775  tr("Show the wallet's balance of the currently selected account."));
2776  m_cmd_binder.set_handler("incoming_transfers",
2777  boost::bind(&simple_wallet::show_incoming_transfers, this, _1),
2778  tr(USAGE_INCOMING_TRANSFERS),
2779  tr("Show the incoming transfers, all or filtered by availability and address index.\n\n"
2780  "Output format:\n"
2781  "Amount, Spent(\"T\"|\"F\"), \"frozen\"|\"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] "));
2782  m_cmd_binder.set_handler("payments",
2783  boost::bind(&simple_wallet::show_payments, this, _1),
2784  tr(USAGE_PAYMENTS),
2785  tr("Show the payments for the given payment IDs."));
2786  m_cmd_binder.set_handler("bc_height",
2787  boost::bind(&simple_wallet::show_blockchain_height, this, _1),
2788  tr("Show the blockchain height."));
2789  m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1),
2790  tr(USAGE_TRANSFER),
2791  tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs in the ring signature. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
2792  m_cmd_binder.set_handler("locked_transfer",
2793  boost::bind(&simple_wallet::locked_transfer, this, _1),
2794  tr(USAGE_LOCKED_TRANSFER),
2795  tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs in the ring signature. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
2796  m_cmd_binder.set_handler("locked_sweep_all",
2797  boost::bind(&simple_wallet::locked_sweep_all, this, _1),
2798  tr(USAGE_LOCKED_SWEEP_ALL),
2799  tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used."));
2800  m_cmd_binder.set_handler("sweep_unmixable",
2801  boost::bind(&simple_wallet::sweep_unmixable, this, _1),
2802  tr("Send all unmixable outputs to yourself with ring_size 1"));
2803  m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1),
2804  tr(USAGE_SWEEP_ALL),
2805  tr("Send all unlocked balance to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
2806  m_cmd_binder.set_handler("sweep_below",
2807  boost::bind(&simple_wallet::sweep_below, this, _1),
2808  tr(USAGE_SWEEP_BELOW),
2809  tr("Send all unlocked outputs below the threshold to an address."));
2810  m_cmd_binder.set_handler("sweep_single",
2811  boost::bind(&simple_wallet::sweep_single, this, _1),
2812  tr(USAGE_SWEEP_SINGLE),
2813  tr("Send a single output of the given key image to an address without change."));
2814  /*
2815  m_cmd_binder.set_handler("donate",
2816  boost::bind(&simple_wallet::donate, this, _1),
2817  tr(USAGE_DONATE),
2818  tr("Donate <amount> to the development team."));
2819  */
2820  m_cmd_binder.set_handler("sign_transfer",
2821  boost::bind(&simple_wallet::sign_transfer, this, _1),
2822  tr(USAGE_SIGN_TRANSFER),
2823  tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported."));
2824  m_cmd_binder.set_handler("submit_transfer",
2825  boost::bind(&simple_wallet::submit_transfer, this, _1),
2826  tr("Submit a signed transaction from a file."));
2827  m_cmd_binder.set_handler("set_log",
2828  boost::bind(&simple_wallet::set_log, this, _1),
2829  tr(USAGE_SET_LOG),
2830  tr("Change the current log detail (level must be <0-4>)."));
2831  m_cmd_binder.set_handler("account",
2832  boost::bind(&simple_wallet::account, this, _1),
2833  tr(USAGE_ACCOUNT),
2834  tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n"
2835  "If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n"
2836  "If the \"switch\" argument is specified, the wallet switches to the account specified by <index>.\n"
2837  "If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text.\n"
2838  "If the \"tag\" argument is specified, a tag <tag_name> is assigned to the specified accounts <account_index_1>, <account_index_2>, ....\n"
2839  "If the \"untag\" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed.\n"
2840  "If the \"tag_description\" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>."));
2841  m_cmd_binder.set_handler("address",
2842  boost::bind(&simple_wallet::print_address, this, _1),
2843  tr(USAGE_ADDRESS),
2844  tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the wallet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text."));
2845  m_cmd_binder.set_handler("integrated_address",
2846  boost::bind(&simple_wallet::print_integrated_address, this, _1),
2847  tr(USAGE_INTEGRATED_ADDRESS),
2848  tr("Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID"));
2849  m_cmd_binder.set_handler("address_book",
2850  boost::bind(&simple_wallet::address_book, this, _1),
2851  tr(USAGE_ADDRESS_BOOK),
2852  tr("Print all entries in the address book, optionally adding/deleting an entry to/from it."));
2853  m_cmd_binder.set_handler("save",
2854  boost::bind(&simple_wallet::save, this, _1),
2855  tr("Save the wallet data."));
2856  m_cmd_binder.set_handler("save_watch_only",
2857  boost::bind(&simple_wallet::save_watch_only, this, _1),
2858  tr("Save a watch-only keys file."));
2859  m_cmd_binder.set_handler("viewkey",
2860  boost::bind(&simple_wallet::viewkey, this, _1),
2861  tr("Display the private view key."));
2862  m_cmd_binder.set_handler("spendkey",
2863  boost::bind(&simple_wallet::spendkey, this, _1),
2864  tr("Display the private spend key."));
2865  m_cmd_binder.set_handler("seed",
2866  boost::bind(&simple_wallet::seed, this, _1),
2867  tr("Display the Electrum-style mnemonic seed"));
2868  m_cmd_binder.set_handler("set",
2869  boost::bind(&simple_wallet::set_variable, this, _1),
2870  tr(USAGE_SET_VARIABLE),
2871  tr("Available options:\n "
2872  "seed language\n "
2873  " Set the wallet's seed language.\n "
2874  "always-confirm-transfers <1|0>\n "
2875  " Whether to confirm unsplit txes.\n "
2876  "print-ring-members <1|0>\n "
2877  " Whether to print detailed information about ring members during confirmation.\n "
2878  "store-tx-info <1|0>\n "
2879  " Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n "
2880  "default-ring-size <n>\n "
2881  " Set the default ring size (obsolete).\n "
2882  "auto-refresh <1|0>\n "
2883  " Whether to automatically synchronize new blocks from the daemon.\n "
2884  "refresh-type <full|optimize-coinbase|no-coinbase|default>\n "
2885  " Set the wallet's refresh behaviour.\n "
2886  "priority [0|1|2|3|4]\n "
2887  " Set the fee to default/unimportant/normal/elevated/priority.\n "
2888  "confirm-missing-payment-id <1|0> (obsolete)\n "
2889  "ask-password <0|1|2 (or never|action|decrypt)>\n "
2890  " action: ask the password before many actions such as transfer, etc\n "
2891  " decrypt: same as action, but keeps the spend key encrypted in memory when not needed\n "
2892  "unit <ETN|cent>\n "
2893  " Set the default Electroneum (sub-)unit.\n "
2894  "min-outputs-count [n]\n "
2895  " Try to keep at least that many outputs of value at least min-outputs-value.\n "
2896  "min-outputs-value [n]\n "
2897  " Try to keep at least min-outputs-count outputs of at least that value.\n "
2898  "merge-destinations <1|0>\n "
2899  " Whether to merge multiple payments to the same destination address.\n "
2900  "confirm-backlog <1|0>\n "
2901  " Whether to warn if there is transaction backlog.\n "
2902  "confirm-backlog-threshold [n]\n "
2903  " Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks.\n "
2904  "refresh-from-block-height [n]\n "
2905  " Set the height before which to ignore blocks.\n "
2906  "auto-low-priority <1|0>\n "
2907  " Whether to automatically use the low priority fee level when it's safe to do so.\n "
2908  "segregate-pre-fork-outputs <1|0>\n "
2909  " Set this if you intend to spend outputs on both Electroneum AND a key reusing fork.\n "
2910  "key-reuse-mitigation2 <1|0>\n "
2911  " Set this if you are not sure whether you will spend on a key reusing Electroneum fork later.\n"
2912  "subaddress-lookahead <major>:<minor>\n "
2913  " Set the lookahead sizes for the subaddress hash table.\n "
2914  " Set this if you are not sure whether you will spend on a key reusing Electroneum fork later.\n "
2915  "segregation-height <n>\n "
2916  " Set to the height of a key reusing fork you want to use, 0 to use default."));
2917  m_cmd_binder.set_handler("encrypted_seed",
2918  boost::bind(&simple_wallet::encrypted_seed, this, _1),
2919  tr("Display the encrypted Electrum-style mnemonic seed."));
2920  m_cmd_binder.set_handler("rescan_spent",
2921  boost::bind(&simple_wallet::rescan_spent, this, _1),
2922  tr("Rescan the blockchain for spent outputs."));
2923  m_cmd_binder.set_handler("get_tx_key",
2924  boost::bind(&simple_wallet::get_tx_key, this, _1),
2925  tr(USAGE_GET_TX_KEY),
2926  tr("Get the transaction key (r) for a given <txid>."));
2927  m_cmd_binder.set_handler("set_tx_key",
2928  boost::bind(&simple_wallet::set_tx_key, this, _1),
2929  tr(USAGE_SET_TX_KEY),
2930  tr("Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet."));
2931  m_cmd_binder.set_handler("check_tx_key",
2932  boost::bind(&simple_wallet::check_tx_key, this, _1),
2933  tr(USAGE_CHECK_TX_KEY),
2934  tr("Check the amount going to <address> in <txid>."));
2935  m_cmd_binder.set_handler("get_tx_proof",
2936  boost::bind(&simple_wallet::get_tx_proof, this, _1),
2937  tr(USAGE_GET_TX_PROOF),
2938  tr("Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key."));
2939  m_cmd_binder.set_handler("check_tx_proof",
2940  boost::bind(&simple_wallet::check_tx_proof, this, _1),
2941  tr(USAGE_CHECK_TX_PROOF),
2942  tr("Check the proof for funds going to <address> in <txid> with the challenge string <message> if any."));
2943  m_cmd_binder.set_handler("get_spend_proof",
2944  boost::bind(&simple_wallet::get_spend_proof, this, _1),
2945  tr(USAGE_GET_SPEND_PROOF),
2946  tr("Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>."));
2947  m_cmd_binder.set_handler("check_spend_proof",
2948  boost::bind(&simple_wallet::check_spend_proof, this, _1),
2949  tr(USAGE_CHECK_SPEND_PROOF),
2950  tr("Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>."));
2951  m_cmd_binder.set_handler("get_reserve_proof",
2952  boost::bind(&simple_wallet::get_reserve_proof, this, _1),
2953  tr(USAGE_GET_RESERVE_PROOF),
2954  tr("Generate a signature proving that you own at least this much, optionally with a challenge string <message>.\n"
2955  "If 'all' is specified, you prove the entire sum of all of your existing accounts' balances.\n"
2956  "Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account."));
2957  m_cmd_binder.set_handler("check_reserve_proof",
2958  boost::bind(&simple_wallet::check_reserve_proof, this, _1),
2959  tr(USAGE_CHECK_RESERVE_PROOF),
2960  tr("Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>."));
2961  m_cmd_binder.set_handler("show_transfers",
2962  boost::bind(&simple_wallet::show_transfers, this, _1),
2963  tr(USAGE_SHOW_TRANSFERS),
2964  // Seemingly broken formatting to compensate for the backslash before the quotes.
2965  tr("Show the incoming/outgoing transfers within an optional height range.\n\n"
2966  "Output format:\n"
2967  "In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n"
2968  "Out: Block Number, \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n"
2969  "Pool: \"pool\", \"in\", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n"
2970  "Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n"
2971  "* Excluding change and fee.\n"
2972  "** Set of address indices used as inputs in this transfer."));
2973  m_cmd_binder.set_handler("export_transfers",
2974  boost::bind(&simple_wallet::export_transfers, this, _1),
2975  tr("export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>]"),
2976  tr("Export to CSV the incoming/outgoing transfers within an optional height range."));
2977  m_cmd_binder.set_handler("unspent_outputs",
2978  boost::bind(&simple_wallet::unspent_outputs, this, _1),
2979  tr(USAGE_UNSPENT_OUTPUTS),
2980  tr("Show the unspent outputs of a specified address within an optional amount range."));
2981  m_cmd_binder.set_handler("rescan_bc",
2982  boost::bind(&simple_wallet::rescan_blockchain, this, _1),
2983  tr(USAGE_RESCAN_BC),
2984  tr("Rescan the blockchain from scratch. If \"hard\" is specified, you will lose any information which can not be recovered from the blockchain itself."));
2985  m_cmd_binder.set_handler("set_tx_note",
2986  boost::bind(&simple_wallet::set_tx_note, this, _1),
2987  tr(USAGE_SET_TX_NOTE),
2988  tr("Set an arbitrary string note for a <txid>."));
2989  m_cmd_binder.set_handler("get_tx_note",
2990  boost::bind(&simple_wallet::get_tx_note, this, _1),
2991  tr(USAGE_GET_TX_NOTE),
2992  tr("Get a string note for a txid."));
2993  m_cmd_binder.set_handler("set_description",
2994  boost::bind(&simple_wallet::set_description, this, _1),
2995  tr(USAGE_SET_DESCRIPTION),
2996  tr("Set an arbitrary description for the wallet."));
2997  m_cmd_binder.set_handler("get_description",
2998  boost::bind(&simple_wallet::get_description, this, _1),
2999  tr(USAGE_GET_DESCRIPTION),
3000  tr("Get the description of the wallet."));
3001  m_cmd_binder.set_handler("status",
3002  boost::bind(&simple_wallet::status, this, _1),
3003  tr("Show the wallet's status."));
3004  m_cmd_binder.set_handler("wallet_info",
3005  boost::bind(&simple_wallet::wallet_info, this, _1),
3006  tr("Show the wallet's information."));
3007  m_cmd_binder.set_handler("sign",
3008  boost::bind(&simple_wallet::sign, this, _1),
3009  tr(USAGE_SIGN),
3010  tr("Sign the contents of a file."));
3011  m_cmd_binder.set_handler("verify",
3012  boost::bind(&simple_wallet::verify, this, _1),
3013  tr(USAGE_VERIFY),
3014  tr("Verify a signature on the contents of a file."));
3015  m_cmd_binder.set_handler("export_key_images",
3016  boost::bind(&simple_wallet::export_key_images, this, _1),
3017  tr(USAGE_EXPORT_KEY_IMAGES),
3018  tr("Export a signed set of key images to a <filename>."));
3019  m_cmd_binder.set_handler("import_key_images",
3020  boost::bind(&simple_wallet::import_key_images, this, _1),
3021  tr(USAGE_IMPORT_KEY_IMAGES),
3022  tr("Import a signed key images list and verify their spent status."));
3023  m_cmd_binder.set_handler("hw_key_images_sync",
3024  boost::bind(&simple_wallet::hw_key_images_sync, this, _1),
3025  tr(USAGE_HW_KEY_IMAGES_SYNC),
3026  tr("Synchronizes key images with the hw wallet."));
3027  m_cmd_binder.set_handler("hw_reconnect",
3028  boost::bind(&simple_wallet::hw_reconnect, this, _1),
3029  tr(USAGE_HW_RECONNECT),
3030  tr("Attempts to reconnect HW wallet."));
3031  m_cmd_binder.set_handler("export_outputs",
3032  boost::bind(&simple_wallet::export_outputs, this, _1),
3033  tr(USAGE_EXPORT_OUTPUTS),
3034  tr("Export a set of outputs owned by this wallet."));
3035  m_cmd_binder.set_handler("import_outputs",
3036  boost::bind(&simple_wallet::import_outputs, this, _1),
3037  tr(USAGE_IMPORT_OUTPUTS),
3038  tr("Import a set of outputs owned by this wallet."));
3039  m_cmd_binder.set_handler("show_transfer",
3040  boost::bind(&simple_wallet::show_transfer, this, _1),
3041  tr(USAGE_SHOW_TRANSFER),
3042  tr("Show information about a transfer to/from this address."));
3043  m_cmd_binder.set_handler("password",
3044  boost::bind(&simple_wallet::change_password, this, _1),
3045  tr("Change the wallet's password."));
3046  m_cmd_binder.set_handler("payment_id",
3047  boost::bind(&simple_wallet::payment_id, this, _1),
3048  tr(USAGE_PAYMENT_ID),
3049  tr("Generate a new random full size payment id (obsolete). These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids."));
3050  m_cmd_binder.set_handler("fee",
3051  boost::bind(&simple_wallet::print_fee_info, this, _1),
3052  tr("Print the information about the current fee and transaction backlog."));
3053  m_cmd_binder.set_handler("prepare_multisig", boost::bind(&simple_wallet::prepare_multisig, this, _1),
3054  tr("Export data needed to create a multisig wallet"));
3055  m_cmd_binder.set_handler("make_multisig", boost::bind(&simple_wallet::make_multisig, this, _1),
3056  tr(USAGE_MAKE_MULTISIG),
3057  tr("Turn this wallet into a multisig wallet"));
3058  m_cmd_binder.set_handler("finalize_multisig",
3059  boost::bind(&simple_wallet::finalize_multisig, this, _1),
3060  tr(USAGE_FINALIZE_MULTISIG),
3061  tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets"));
3062  m_cmd_binder.set_handler("exchange_multisig_keys",
3063  boost::bind(&simple_wallet::exchange_multisig_keys, this, _1),
3064  tr(USAGE_EXCHANGE_MULTISIG_KEYS),
3065  tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets"));
3066  m_cmd_binder.set_handler("export_multisig_info",
3067  boost::bind(&simple_wallet::export_multisig, this, _1),
3068  tr(USAGE_EXPORT_MULTISIG_INFO),
3069  tr("Export multisig info for other participants"));
3070  m_cmd_binder.set_handler("import_multisig_info",
3071  boost::bind(&simple_wallet::import_multisig, this, _1),
3072  tr(USAGE_IMPORT_MULTISIG_INFO),
3073  tr("Import multisig info from other participants"));
3074  m_cmd_binder.set_handler("sign_multisig",
3075  boost::bind(&simple_wallet::sign_multisig, this, _1),
3076  tr(USAGE_SIGN_MULTISIG),
3077  tr("Sign a multisig transaction from a file"));
3078  m_cmd_binder.set_handler("submit_multisig",
3079  boost::bind(&simple_wallet::submit_multisig, this, _1),
3080  tr(USAGE_SUBMIT_MULTISIG),
3081  tr("Submit a signed multisig transaction from a file"));
3082  m_cmd_binder.set_handler("export_raw_multisig_tx",
3083  boost::bind(&simple_wallet::export_raw_multisig, this, _1),
3084  tr(USAGE_EXPORT_RAW_MULTISIG_TX),
3085  tr("Export a signed multisig transaction to a file"));
3086  m_cmd_binder.set_handler("mms",
3087  boost::bind(&simple_wallet::mms, this, _1),
3088  tr(USAGE_MMS),
3089  tr("Interface with the MMS (Multisig Messaging System)\n"
3090  "<subcommand> is one of:\n"
3091  " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n"
3092  " send_signer_config, start_auto_config, stop_auto_config, auto_config\n"
3093  "Get help about a subcommand with: help mms <subcommand>, or mms help <subcommand>"));
3094  m_cmd_binder.set_handler("mms init",
3095  boost::bind(&simple_wallet::mms, this, _1),
3096  tr(USAGE_MMS_INIT),
3097  tr("Initialize and configure the MMS for M/N = number of required signers/number of authorized signers multisig"));
3098  m_cmd_binder.set_handler("mms info",
3099  boost::bind(&simple_wallet::mms, this, _1),
3100  tr(USAGE_MMS_INFO),
3101  tr("Display current MMS configuration"));
3102  m_cmd_binder.set_handler("mms signer",
3103  boost::bind(&simple_wallet::mms, this, _1),
3104  tr(USAGE_MMS_SIGNER),
3105  tr("Set or modify authorized signer info (single-word label, transport address, Electroneum address), or list all signers"));
3106  m_cmd_binder.set_handler("mms list",
3107  boost::bind(&simple_wallet::mms, this, _1),
3108  tr(USAGE_MMS_LIST),
3109  tr("List all messages"));
3110  m_cmd_binder.set_handler("mms next",
3111  boost::bind(&simple_wallet::mms, this, _1),
3112  tr(USAGE_MMS_NEXT),
3113  tr("Evaluate the next possible multisig-related action(s) according to wallet state, and execute or offer for choice\n"
3114  "By using 'sync' processing of waiting messages with multisig sync info can be forced regardless of wallet state"));
3115  m_cmd_binder.set_handler("mms sync",
3116  boost::bind(&simple_wallet::mms, this, _1),
3117  tr(USAGE_MMS_SYNC),
3118  tr("Force generation of multisig sync info regardless of wallet state, to recover from special situations like \"stale data\" errors"));
3119  m_cmd_binder.set_handler("mms transfer",
3120  boost::bind(&simple_wallet::mms, this, _1),
3121  tr(USAGE_MMS_TRANSFER),
3122  tr("Initiate transfer with MMS support; arguments identical to normal 'transfer' command arguments, for info see there"));
3123  m_cmd_binder.set_handler("mms delete",
3124  boost::bind(&simple_wallet::mms, this, _1),
3125  tr(USAGE_MMS_DELETE),
3126  tr("Delete a single message by giving its id, or delete all messages by using 'all'"));
3127  m_cmd_binder.set_handler("mms send",
3128  boost::bind(&simple_wallet::mms, this, _1),
3129  tr(USAGE_MMS_SEND),
3130  tr("Send a single message by giving its id, or send all waiting messages"));
3131  m_cmd_binder.set_handler("mms receive",
3132  boost::bind(&simple_wallet::mms, this, _1),
3133  tr(USAGE_MMS_RECEIVE),
3134  tr("Check right away for new messages to receive"));
3135  m_cmd_binder.set_handler("mms export",
3136  boost::bind(&simple_wallet::mms, this, _1),
3137  tr(USAGE_MMS_EXPORT),
3138  tr("Write the content of a message to a file \"mms_message_content\""));
3139  m_cmd_binder.set_handler("mms note",
3140  boost::bind(&simple_wallet::mms, this, _1),
3141  tr(USAGE_MMS_NOTE),
3142  tr("Send a one-line message to an authorized signer, identified by its label, or show any waiting unread notes"));
3143  m_cmd_binder.set_handler("mms show",
3144  boost::bind(&simple_wallet::mms, this, _1),
3145  tr(USAGE_MMS_SHOW),
3146  tr("Show detailed info about a single message"));
3147  m_cmd_binder.set_handler("mms set",
3148  boost::bind(&simple_wallet::mms, this, _1),
3149  tr(USAGE_MMS_SET),
3150  tr("Available options:\n "
3151  "auto-send <1|0>\n "
3152  " Whether to automatically send newly generated messages right away.\n "));
3153  m_cmd_binder.set_handler("mms send_message_config",
3154  boost::bind(&simple_wallet::mms, this, _1),
3155  tr(USAGE_MMS_SEND_SIGNER_CONFIG),
3156  tr("Send completed signer config to all other authorized signers"));
3157  m_cmd_binder.set_handler("mms start_auto_config",
3158  boost::bind(&simple_wallet::mms, this, _1),
3159  tr(USAGE_MMS_START_AUTO_CONFIG),
3160  tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels"));
3161  m_cmd_binder.set_handler("mms stop_auto_config",
3162  boost::bind(&simple_wallet::mms, this, _1),
3163  tr(USAGE_MMS_STOP_AUTO_CONFIG),
3164  tr("Delete any auto-config tokens and abort a auto-config process"));
3165  m_cmd_binder.set_handler("mms auto_config",
3166  boost::bind(&simple_wallet::mms, this, _1),
3167  tr(USAGE_MMS_AUTO_CONFIG),
3168  tr("Start auto-config by using the token received from the auto-config manager"));
3169  m_cmd_binder.set_handler("print_ring",
3170  boost::bind(&simple_wallet::print_ring, this, _1),
3171  tr(USAGE_PRINT_RING),
3172  tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)\n\n"
3173  "Output format:\n"
3174  "Key Image, \"absolute\", list of rings"));
3175  m_cmd_binder.set_handler("set_ring",
3176  boost::bind(&simple_wallet::set_ring, this, _1),
3177  tr(USAGE_SET_RING),
3178  tr("Set the ring used for a given key image, so it can be reused in a fork"));
3179  m_cmd_binder.set_handler("unset_ring",
3180  boost::bind(&simple_wallet::unset_ring, this, _1),
3181  tr(USAGE_UNSET_RING),
3182  tr("Unsets the ring used for a given key image or transaction"));
3183  m_cmd_binder.set_handler("save_known_rings",
3184  boost::bind(&simple_wallet::save_known_rings, this, _1),
3185  tr(USAGE_SAVE_KNOWN_RINGS),
3186  tr("Save known rings to the shared rings database"));
3187  m_cmd_binder.set_handler("mark_output_spent",
3188  boost::bind(&simple_wallet::blackball, this, _1),
3189  tr(USAGE_MARK_OUTPUT_SPENT),
3190  tr("Mark output(s) as spent so they never get selected as fake outputs in a ring"));
3191  m_cmd_binder.set_handler("mark_output_unspent",
3192  boost::bind(&simple_wallet::unblackball, this, _1),
3193  tr(USAGE_MARK_OUTPUT_UNSPENT),
3194  tr("Marks an output as unspent so it may get selected as a fake output in a ring"));
3195  m_cmd_binder.set_handler("is_output_spent",
3196  boost::bind(&simple_wallet::blackballed, this, _1),
3197  tr(USAGE_IS_OUTPUT_SPENT),
3198  tr("Checks whether an output is marked as spent"));
3199  m_cmd_binder.set_handler("freeze",
3200  boost::bind(&simple_wallet::freeze, this, _1),
3201  tr(USAGE_FREEZE),
3202  tr("Freeze a single output by key image so it will not be used"));
3203  m_cmd_binder.set_handler("thaw",
3204  boost::bind(&simple_wallet::thaw, this, _1),
3205  tr(USAGE_THAW),
3206  tr("Thaw a single output by key image so it may be used again"));
3207  m_cmd_binder.set_handler("frozen",
3208  boost::bind(&simple_wallet::frozen, this, _1),
3209  tr(USAGE_FROZEN),
3210  tr("Checks whether a given output is currently frozen by key image"));
3211  m_cmd_binder.set_handler("net_stats",
3212  boost::bind(&simple_wallet::net_stats, this, _1),
3213  tr(USAGE_NET_STATS),
3214  tr("Prints simple network stats"));
3215  m_cmd_binder.set_handler("welcome",
3216  boost::bind(&simple_wallet::welcome, this, _1),
3217  tr(USAGE_WELCOME),
3218  tr("Prints basic info about Electroneum for first time users"));
3219  m_cmd_binder.set_handler("version",
3220  boost::bind(&simple_wallet::version, this, _1),
3221  tr(USAGE_VERSION),
3222  tr("Returns version information"));
3223  m_cmd_binder.set_handler("help",
3224  boost::bind(&simple_wallet::help, this, _1),
3225  tr(USAGE_HELP),
3226  tr("Show the help section or the documentation about a <command>."));
3227 }
3228 //----------------------------------------------------------------------------------------------------
3229 bool simple_wallet::set_variable(const std::vector<std::string> &args)
3230 {
3231  if (args.empty())
3232  {
3233  std::string seed_language = m_wallet->get_seed_language();
3234  if (m_use_english_language_names)
3235  seed_language = crypto::ElectrumWords::get_english_name_for(seed_language);
3236  std::string priority_string = "invalid";
3237  uint32_t priority = m_wallet->get_default_priority();
3238  if (priority < allowed_priority_strings.size())
3239  priority_string = allowed_priority_strings[priority];
3240  std::string ask_password_string = "invalid";
3241  switch (m_wallet->ask_password())
3242  {
3243  case tools::wallet2::AskPasswordNever: ask_password_string = "never"; break;
3244  case tools::wallet2::AskPasswordOnAction: ask_password_string = "action"; break;
3245  case tools::wallet2::AskPasswordToDecrypt: ask_password_string = "decrypt"; break;
3246  }
3247  std::string setup_background_mining_string = "invalid";
3248  switch (m_wallet->setup_background_mining())
3249  {
3250  case tools::wallet2::BackgroundMiningMaybe: setup_background_mining_string = "maybe"; break;
3251  case tools::wallet2::BackgroundMiningYes: setup_background_mining_string = "yes"; break;
3252  case tools::wallet2::BackgroundMiningNo: setup_background_mining_string = "no"; break;
3253  }
3254  success_msg_writer() << "seed = " << seed_language;
3255  success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers();
3256  success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members();
3257  success_msg_writer() << "store-tx-info = " << m_wallet->store_tx_info();
3258  success_msg_writer() << "default-ring-size = " << (m_wallet->default_mixin() ? m_wallet->default_mixin() + 1 : 0);
3259  success_msg_writer() << "auto-refresh = " << m_wallet->auto_refresh();
3260  success_msg_writer() << "refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type());
3261  success_msg_writer() << "priority = " << priority<< " (" << priority_string << ")";
3262  success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id();
3263  success_msg_writer() << "ask-password = " << m_wallet->ask_password() << " (" << ask_password_string << ")";
3265  success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count();
3266  success_msg_writer() << "min-outputs-value = " << cryptonote::print_etn(m_wallet->get_min_output_value());
3267  success_msg_writer() << "merge-destinations = " << m_wallet->merge_destinations();
3268  success_msg_writer() << "confirm-backlog = " << m_wallet->confirm_backlog();
3269  success_msg_writer() << "confirm-backlog-threshold = " << m_wallet->get_confirm_backlog_threshold();
3270  success_msg_writer() << "confirm-export-overwrite = " << m_wallet->confirm_export_overwrite();
3271  success_msg_writer() << "refresh-from-block-height = " << m_wallet->get_refresh_from_block_height();
3272  success_msg_writer() << "auto-low-priority = " << m_wallet->auto_low_priority();
3273  success_msg_writer() << "segregate-pre-fork-outputs = " << m_wallet->segregate_pre_fork_outputs();
3274  success_msg_writer() << "key-reuse-mitigation2 = " << m_wallet->key_reuse_mitigation2();
3275  const std::pair<size_t, size_t> lookahead = m_wallet->get_subaddress_lookahead();
3276  success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second;
3277  success_msg_writer() << "segregation-height = " << m_wallet->segregation_height();
3278  success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
3279  success_msg_writer() << "track-uses = " << m_wallet->track_uses();
3280  //success_msg_writer() << "setup-background-mining = " << setup_background_mining_string + tr(" (set this to support the network and to get a chance to receive new Electroneum)");
3281  success_msg_writer() << "device_name = " << m_wallet->device_name();
3282  success_msg_writer() << "account-major-offset = " << m_wallet->account_major_offset();
3283  return true;
3284  }
3285  else
3286  {
3287 
3288 #define CHECK_SIMPLE_VARIABLE(name, f, help) do \
3289  if (args[0] == name) { \
3290  if (args.size() <= 1) \
3291  { \
3292  fail_msg_writer() << "set " << #name << ": " << tr("needs an argument") << " (" << help << ")"; \
3293  return true; \
3294  } \
3295  else \
3296  { \
3297  f(args); \
3298  return true; \
3299  } \
3300  } while(0)
3301 
3302  if (args[0] == "seed")
3303  {
3304  if (args.size() == 1)
3305  {
3306  fail_msg_writer() << tr("set seed: needs an argument. available options: language");
3307  return true;
3308  }
3309  else if (args[1] == "language")
3310  {
3311  seed_set_language(args);
3312  return true;
3313  }
3314  }
3315  CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1"));
3316  CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1"));
3317  CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1"));
3318  CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE);
3319  CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1"));
3320  CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)"));
3321  CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", "));
3322  CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1"));
3323  CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0|1|2 (or never|action|decrypt)"));
3324  CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("ETN, cent"));
3325  CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer"));
3326  CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount"));
3327  CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1"));
3328  CHECK_SIMPLE_VARIABLE("confirm-backlog", set_confirm_backlog, tr("0 or 1"));
3329  CHECK_SIMPLE_VARIABLE("confirm-backlog-threshold", set_confirm_backlog_threshold, tr("unsigned integer"));
3330  CHECK_SIMPLE_VARIABLE("confirm-export-overwrite", set_confirm_export_overwrite, tr("0 or 1"));
3331  CHECK_SIMPLE_VARIABLE("refresh-from-block-height", set_refresh_from_block_height, tr("block height"));
3332  CHECK_SIMPLE_VARIABLE("auto-low-priority", set_auto_low_priority, tr("0 or 1"));
3333  CHECK_SIMPLE_VARIABLE("segregate-pre-fork-outputs", set_segregate_pre_fork_outputs, tr("0 or 1"));
3334  CHECK_SIMPLE_VARIABLE("key-reuse-mitigation2", set_key_reuse_mitigation2, tr("0 or 1"));
3335  CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
3336  CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
3337  CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
3338  CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1"));
3339  CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no"));
3340  CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
3341  CHECK_SIMPLE_VARIABLE("account-major-offset", set_account_major_offset, tr("offset"));
3342  }
3343  fail_msg_writer() << tr("set: unrecognized argument(s)");
3344  return true;
3345 }
3346 
3347 //----------------------------------------------------------------------------------------------------
3348 bool simple_wallet::set_log(const std::vector<std::string> &args)
3349 {
3350  if(args.size() > 1)
3351  {
3352  PRINT_USAGE(USAGE_SET_LOG);
3353  return true;
3354  }
3355  if(!args.empty())
3356  {
3357  uint16_t level = 0;
3358  if(epee::string_tools::get_xtype_from_string(level, args[0]))
3359  {
3360  if(4 < level)
3361  {
3362  fail_msg_writer() << boost::format(tr("wrong number range, use: %s")) % USAGE_SET_LOG;
3363  return true;
3364  }
3365  mlog_set_log_level(level);
3366  }
3367  else
3368  {
3369  mlog_set_log(args[0].c_str());
3370  }
3371  }
3372 
3373  success_msg_writer() << "New log categories: " << mlog_get_categories();
3374  return true;
3375 }
3376 //----------------------------------------------------------------------------------------------------
3377 bool simple_wallet::ask_wallet_create_if_needed()
3378 {
3379  LOG_PRINT_L3("simple_wallet::ask_wallet_create_if_needed() started");
3380  std::string wallet_path;
3381  std::string confirm_creation;
3382  bool wallet_name_valid = false;
3383  bool keys_file_exists;
3384  bool wallet_file_exists;
3385 
3386  do{
3387  LOG_PRINT_L3("User asked to specify wallet file name.");
3388  wallet_path = input_line(
3389  tr(m_restoring ? "Specify a new wallet file name for your restored wallet (e.g., MyWallet).\n"
3390  "Wallet file name (or Ctrl-C to quit)" :
3391  "Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n"
3392  "Wallet file name (or Ctrl-C to quit)")
3393  );
3394  if(std::cin.eof())
3395  {
3396  LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
3397  return false;
3398  }
3400  {
3401  fail_msg_writer() << tr("Wallet name not valid. Please try again or use Ctrl-C to quit.");
3402  wallet_name_valid = false;
3403  }
3404  else
3405  {
3406  tools::wallet2::wallet_exists(wallet_path, keys_file_exists, wallet_file_exists);
3407  LOG_PRINT_L3("wallet_path: " << wallet_path << "");
3408  LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha
3409  << " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha);
3410 
3411  if((keys_file_exists || wallet_file_exists) && (!m_generate_new.empty() || m_restoring))
3412  {
3413  fail_msg_writer() << tr("Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.");
3414  return false;
3415  }
3416  if(wallet_file_exists && keys_file_exists) //Yes wallet, yes keys
3417  {
3418  success_msg_writer() << tr("Wallet and key files found, loading...");
3419  m_wallet_file = wallet_path;
3420  return true;
3421  }
3422  else if(!wallet_file_exists && keys_file_exists) //No wallet, yes keys
3423  {
3424  success_msg_writer() << tr("Key file found but not wallet file. Regenerating...");
3425  m_wallet_file = wallet_path;
3426  return true;
3427  }
3428  else if(wallet_file_exists && !keys_file_exists) //Yes wallet, no keys
3429  {
3430  fail_msg_writer() << tr("Key file not found. Failed to open wallet: ") << "\"" << wallet_path << "\". Exiting.";
3431  return false;
3432  }
3433  else if(!wallet_file_exists && !keys_file_exists) //No wallet, no keys
3434  {
3435  bool ok = true;
3436  if (!m_restoring)
3437  {
3438  message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path;
3439  confirm_creation = input_line("", true);
3440  if(std::cin.eof())
3441  {
3442  LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
3443  return false;
3444  }
3445  ok = command_line::is_yes(confirm_creation);
3446  }
3447  if (ok)
3448  {
3449  success_msg_writer() << tr("Generating new wallet...");
3450  m_generate_new = wallet_path;
3451  return true;
3452  }
3453  }
3454  }
3455  } while(!wallet_name_valid);
3456 
3457  LOG_ERROR("Failed out of do-while loop in ask_wallet_create_if_needed()");
3458  return false;
3459 }
3460 
3465 void simple_wallet::print_seed(const epee::wipeable_string &seed)
3466 {
3467  success_msg_writer(true) << "\n" << boost::format(tr("NOTE: the following %s can be used to recover access to your wallet. "
3468  "Write them down and store them somewhere safe and secure. Please do not store them in "
3469  "your email or on file storage services outside of your immediate control.\n")) % (m_wallet->multisig() ? tr("string") : tr("25 words"));
3470  // don't log
3471  int space_index = 0;
3472  size_t len = seed.size();
3473  for (const char *ptr = seed.data(); len--; ++ptr)
3474  {
3475  if (*ptr == ' ')
3476  {
3477  if (space_index == 15 || space_index == 7)
3478  putchar('\n');
3479  else
3480  putchar(*ptr);
3481  ++space_index;
3482  }
3483  else
3484  putchar(*ptr);
3485  }
3486  putchar('\n');
3487  fflush(stdout);
3488 }
3489 //----------------------------------------------------------------------------------------------------
3490 static bool might_be_partial_seed(const epee::wipeable_string &words)
3491 {
3492  std::vector<epee::wipeable_string> seed;
3493 
3494  words.split(seed);
3495  return seed.size() < 24;
3496 }
3497 //----------------------------------------------------------------------------------------------------
3498 static bool datestr_to_int(const std::string &heightstr, uint16_t &year, uint8_t &month, uint8_t &day)
3499 {
3500  if (heightstr.size() != 10 || heightstr[4] != '-' || heightstr[7] != '-')
3501  {
3502  fail_msg_writer() << tr("date format must be YYYY-MM-DD");
3503  return false;
3504  }
3505  try
3506  {
3507  year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4));
3508  // lexical_cast<uint8_t> won't work because uint8_t is treated as character type
3509  month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2));
3510  day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2));
3511  }
3512  catch (const boost::bad_lexical_cast &)
3513  {
3514  fail_msg_writer() << tr("bad height parameter: ") << heightstr;
3515  return false;
3516  }
3517  return true;
3518 }
3519 //----------------------------------------------------------------------------------------------------
3520 bool simple_wallet::init(const boost::program_options::variables_map& vm)
3521 {
3523  m_electrum_seed.wipe();
3524  });
3525 
3526  const bool testnet = tools::wallet2::has_testnet_option(vm);
3527  const bool stagenet = tools::wallet2::has_stagenet_option(vm);
3528  if (testnet && stagenet)
3529  {
3530  fail_msg_writer() << tr("Can't specify more than one of --testnet and --stagenet");
3531  return false;
3532  }
3533  const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
3534 
3535  epee::wipeable_string multisig_keys;
3536  epee::wipeable_string password;
3537 
3538  if (!handle_command_line(vm))
3539  return false;
3540 
3541  bool welcome = false;
3542 
3543  if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_device.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1)
3544  {
3545  fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-device=\"wallet_name\"");
3546  return false;
3547  }
3548  else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_device.empty() && m_generate_from_view_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty())
3549  {
3550  if(!ask_wallet_create_if_needed()) return false;
3551  }
3552 
3553  if (!m_generate_new.empty() || m_restoring)
3554  {
3555  if (!m_subaddress_lookahead.empty() && !parse_subaddress_lookahead(m_subaddress_lookahead))
3556  return false;
3557 
3558  std::string old_language;
3559  // check for recover flag. if present, require electrum word list (only recovery option for now).
3560  if (m_restore_deterministic_wallet || m_restore_multisig_wallet)
3561  {
3562  if (m_non_deterministic)
3563  {
3564  fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic");
3565  return false;
3566  }
3567  if (!m_wallet_file.empty())
3568  {
3569  if (m_restore_multisig_wallet)
3570  fail_msg_writer() << tr("--restore-multisig-wallet uses --generate-new-wallet, not --wallet-file");
3571  else
3572  fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
3573  return false;
3574  }
3575 
3576  if (m_electrum_seed.empty())
3577  {
3578  if (m_restore_multisig_wallet)
3579  {
3580  const char *prompt = "Specify multisig seed";
3581  m_electrum_seed = input_secure_line(prompt);
3582  if (std::cin.eof())
3583  return false;
3584  if (m_electrum_seed.empty())
3585  {
3586  fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"multisig seed here\"");
3587  return false;
3588  }
3589  }
3590  else
3591  {
3592  m_electrum_seed = "";
3593  do
3594  {
3595  const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed" : "Electrum seed continued";
3596  epee::wipeable_string electrum_seed = input_secure_line(prompt);
3597  if (std::cin.eof())
3598  return false;
3599  if (electrum_seed.empty())
3600  {
3601  fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
3602  return false;
3603  }
3604  m_electrum_seed += electrum_seed;
3605  m_electrum_seed += ' ';
3606  } while (might_be_partial_seed(m_electrum_seed));
3607  }
3608  }
3609 
3610  if (m_restore_multisig_wallet)
3611  {
3612  const boost::optional<epee::wipeable_string> parsed = m_electrum_seed.parse_hexstr();
3613  if (!parsed)
3614  {
3615  fail_msg_writer() << tr("Multisig seed failed verification");
3616  return false;
3617  }
3618  multisig_keys = *parsed;
3619  }
3620  else
3621  {
3622  if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
3623  {
3624  fail_msg_writer() << tr("Electrum-style word list failed verification");
3625  return false;
3626  }
3627  }
3628 
3629  auto pwd_container = password_prompter(tr("Enter seed offset passphrase, empty if none"), false);
3630  if (std::cin.eof() || !pwd_container)
3631  return false;
3632  epee::wipeable_string seed_pass = pwd_container->password();
3633  if (!seed_pass.empty())
3634  {
3635  if (m_restore_multisig_wallet)
3636  {
3638  crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
3639  sc_reduce32((unsigned char*)key.data);
3640  multisig_keys = m_wallet->decrypt<epee::wipeable_string>(std::string(multisig_keys.data(), multisig_keys.size()), key, true);
3641  }
3642  else
3643  m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
3644  }
3645  }
3646  if (!m_generate_from_view_key.empty())
3647  {
3648  m_wallet_file = m_generate_from_view_key;
3649  // parse address
3650  std::string address_string = input_line("Standard address");
3651  if (std::cin.eof())
3652  return false;
3653  if (address_string.empty()) {
3654  fail_msg_writer() << tr("No data supplied, cancelled");
3655  return false;
3656  }
3658  if(!get_account_address_from_str(info, nettype, address_string))
3659  {
3660  fail_msg_writer() << tr("failed to parse address");
3661  return false;
3662  }
3663  if (info.is_subaddress)
3664  {
3665  fail_msg_writer() << tr("This address is a subaddress which cannot be used here.");
3666  return false;
3667  }
3668 
3669  // parse view secret key
3670  epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
3671  if (std::cin.eof())
3672  return false;
3673  if (viewkey_string.empty()) {
3674  fail_msg_writer() << tr("No data supplied, cancelled");
3675  return false;
3676  }
3677  crypto::secret_key viewkey;
3678  if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
3679  {
3680  fail_msg_writer() << tr("failed to parse view key secret key");
3681  return false;
3682  }
3683 
3684  m_wallet_file=m_generate_from_view_key;
3685 
3686  // check the view key matches the given address
3687  crypto::public_key pkey;
3688  if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
3689  fail_msg_writer() << tr("failed to verify view key secret key");
3690  return false;
3691  }
3692  if (info.address.m_view_public_key != pkey) {
3693  fail_msg_writer() << tr("view key does not match standard address");
3694  return false;
3695  }
3696 
3697  auto r = new_wallet(vm, info.address, boost::none, viewkey);
3698  CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
3699  password = *r;
3700  welcome = true;
3701  }
3702  else if (!m_generate_from_spend_key.empty())
3703  {
3704  m_wallet_file = m_generate_from_spend_key;
3705  // parse spend secret key
3706  epee::wipeable_string spendkey_string = input_secure_line("Secret spend key");
3707  if (std::cin.eof())
3708  return false;
3709  if (spendkey_string.empty()) {
3710  fail_msg_writer() << tr("No data supplied, cancelled");
3711  return false;
3712  }
3713  if (!spendkey_string.hex_to_pod(unwrap(unwrap(m_recovery_key))))
3714  {
3715  fail_msg_writer() << tr("failed to parse spend key secret key");
3716  return false;
3717  }
3718  auto r = new_wallet(vm, m_recovery_key, true, false, "");
3719  CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
3720  password = *r;
3721  welcome = true;
3722  }
3723  else if (!m_generate_from_keys.empty())
3724  {
3725  m_wallet_file = m_generate_from_keys;
3726  // parse address
3727  std::string address_string = input_line("Standard address");
3728  if (std::cin.eof())
3729  return false;
3730  if (address_string.empty()) {
3731  fail_msg_writer() << tr("No data supplied, cancelled");
3732  return false;
3733  }
3735  if(!get_account_address_from_str(info, nettype, address_string))
3736  {
3737  fail_msg_writer() << tr("failed to parse address");
3738  return false;
3739  }
3740  if (info.is_subaddress)
3741  {
3742  fail_msg_writer() << tr("This address is a subaddress which cannot be used here.");
3743  return false;
3744  }
3745 
3746  // parse spend secret key
3747  epee::wipeable_string spendkey_string = input_secure_line("Secret spend key");
3748  if (std::cin.eof())
3749  return false;
3750  if (spendkey_string.empty()) {
3751  fail_msg_writer() << tr("No data supplied, cancelled");
3752  return false;
3753  }
3755  if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey))))
3756  {
3757  fail_msg_writer() << tr("failed to parse spend key secret key");
3758  return false;
3759  }
3760 
3761  // parse view secret key
3762  epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
3763  if (std::cin.eof())
3764  return false;
3765  if (viewkey_string.empty()) {
3766  fail_msg_writer() << tr("No data supplied, cancelled");
3767  return false;
3768  }
3769  crypto::secret_key viewkey;
3770  if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
3771  {
3772  fail_msg_writer() << tr("failed to parse view key secret key");
3773  return false;
3774  }
3775 
3776  m_wallet_file=m_generate_from_keys;
3777 
3778  // check the spend and view keys match the given address
3779  crypto::public_key pkey;
3781  fail_msg_writer() << tr("failed to verify spend key secret key");
3782  return false;
3783  }
3784  if (info.address.m_spend_public_key != pkey) {
3785  fail_msg_writer() << tr("spend key does not match standard address");
3786  return false;
3787  }
3788  if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
3789  fail_msg_writer() << tr("failed to verify view key secret key");
3790  return false;
3791  }
3792  if (info.address.m_view_public_key != pkey) {
3793  fail_msg_writer() << tr("view key does not match standard address");
3794  return false;
3795  }
3796  auto r = new_wallet(vm, info.address, spendkey, viewkey);
3797  CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
3798  password = *r;
3799  welcome = true;
3800  }
3801 
3802  // Asks user for all the data required to merge secret keys from multisig wallets into one master wallet, which then gets full control of the multisig wallet. The resulting wallet will be the same as any other regular wallet.
3803  else if (!m_generate_from_multisig_keys.empty())
3804  {
3805  m_wallet_file = m_generate_from_multisig_keys;
3806  unsigned int multisig_m;
3807  unsigned int multisig_n;
3808 
3809  // parse multisig type
3810  std::string multisig_type_string = input_line("Multisig type (input as M/N with M <= N and M > 1)");
3811  if (std::cin.eof())
3812  return false;
3813  if (multisig_type_string.empty())
3814  {
3815  fail_msg_writer() << tr("No data supplied, cancelled");
3816  return false;
3817  }
3818  if (sscanf(multisig_type_string.c_str(), "%u/%u", &multisig_m, &multisig_n) != 2)
3819  {
3820  fail_msg_writer() << tr("Error: expected M/N, but got: ") << multisig_type_string;
3821  return false;
3822  }
3823  if (multisig_m <= 1 || multisig_m > multisig_n)
3824  {
3825  fail_msg_writer() << tr("Error: expected N > 1 and N <= M, but got: ") << multisig_type_string;
3826  return false;
3827  }
3828  if (multisig_m != multisig_n)
3829  {
3830  fail_msg_writer() << tr("Error: M/N is currently unsupported. ");
3831  return false;
3832  }
3833  message_writer() << boost::format(tr("Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n;
3834 
3835  // parse multisig address
3836  std::string address_string = input_line("Multisig wallet address");
3837  if (std::cin.eof())
3838  return false;
3839  if (address_string.empty()) {
3840  fail_msg_writer() << tr("No data supplied, cancelled");
3841  return false;
3842  }
3844  if(!get_account_address_from_str(info, nettype, address_string))
3845  {
3846  fail_msg_writer() << tr("failed to parse address");
3847  return false;
3848  }
3849 
3850  // parse secret view key
3851  epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
3852  if (std::cin.eof())
3853  return false;
3854  if (viewkey_string.empty())
3855  {
3856  fail_msg_writer() << tr("No data supplied, cancelled");
3857  return false;
3858  }
3859  crypto::secret_key viewkey;
3860  if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
3861  {
3862  fail_msg_writer() << tr("failed to parse secret view key");
3863  return false;
3864  }
3865 
3866  // check that the view key matches the given address
3867  crypto::public_key pkey;
3868  if (!crypto::secret_key_to_public_key(viewkey, pkey))
3869  {
3870  fail_msg_writer() << tr("failed to verify secret view key");
3871  return false;
3872  }
3873  if (info.address.m_view_public_key != pkey)
3874  {
3875  fail_msg_writer() << tr("view key does not match standard address");
3876  return false;
3877  }
3878 
3879  // parse multisig spend keys
3881  // parsing N/N
3882  if(multisig_m == multisig_n)
3883  {
3884  std::vector<crypto::secret_key> multisig_secret_spendkeys(multisig_n);
3885  epee::wipeable_string spendkey_string;
3886  cryptonote::blobdata spendkey_data;
3887  // get N secret spend keys from user
3888  for(unsigned int i=0; i<multisig_n; ++i)
3889  {
3890  spendkey_string = input_secure_line(tr((boost::format(tr("Secret spend key (%u of %u)")) % (i+1) % multisig_m).str().c_str()));
3891  if (std::cin.eof())
3892  return false;
3893  if (spendkey_string.empty())
3894  {
3895  fail_msg_writer() << tr("No data supplied, cancelled");
3896  return false;
3897  }
3898  if(!spendkey_string.hex_to_pod(unwrap(unwrap(multisig_secret_spendkeys[i]))))
3899  {
3900  fail_msg_writer() << tr("failed to parse spend key secret key");
3901  return false;
3902  }
3903  }
3904 
3905  // sum the spend keys together to get the master spend key
3906  spendkey = multisig_secret_spendkeys[0];
3907  for(unsigned int i=1; i<multisig_n; ++i)
3908  sc_add(reinterpret_cast<unsigned char*>(&spendkey), reinterpret_cast<unsigned char*>(&spendkey), reinterpret_cast<unsigned char*>(&multisig_secret_spendkeys[i]));
3909  }
3910  // parsing M/N
3911  else
3912  {
3913  fail_msg_writer() << tr("Error: M/N is currently unsupported");
3914  return false;
3915  }
3916 
3917  // check that the spend key matches the given address
3919  {
3920  fail_msg_writer() << tr("failed to verify spend key secret key");
3921  return false;
3922  }
3923  if (info.address.m_spend_public_key != pkey)
3924  {
3925  fail_msg_writer() << tr("spend key does not match standard address");
3926  return false;
3927  }
3928 
3929  // create wallet
3930  auto r = new_wallet(vm, info.address, spendkey, viewkey);
3931  CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
3932  password = *r;
3933  welcome = true;
3934  }
3935 
3936  else if (!m_generate_from_json.empty())
3937  {
3938  try
3939  {
3940  auto rc = tools::wallet2::make_from_json(vm, false, m_generate_from_json, password_prompter);
3941  m_wallet = std::move(rc.first);
3942  password = rc.second.password();
3943  m_wallet_file = m_wallet->path();
3944  }
3945  catch (const std::exception &e)
3946  {
3947  fail_msg_writer() << e.what();
3948  return false;
3949  }
3950  if (!m_wallet)
3951  return false;
3952  }
3953  else if (!m_generate_from_device.empty())
3954  {
3955  m_wallet_file = m_generate_from_device;
3956  // create wallet
3957  auto r = new_wallet(vm);
3958  CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
3959  password = *r;
3960  welcome = true;
3961  // if no block_height is specified, assume its a new account and start it "now"
3962  if(m_wallet->get_refresh_from_block_height() == 0) {
3963  {
3965  wrt << tr("No restore height is specified.") << " ";
3966  wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height.") << " ";
3967  wrt << tr("Use --restore-height or --restore-date if you want to restore an already setup account from a specific height.");
3968  }
3969  std::string confirm = input_line(tr("Is this okay?"), true);
3970  if (std::cin.eof() || !command_line::is_yes(confirm))
3971  CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted"));
3972 
3973  m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height());
3974  m_wallet->explicit_refresh_from_block_height(true);
3975  m_restore_height = m_wallet->get_refresh_from_block_height();
3976  m_wallet->always_confirm_transfers(true);
3977  }
3978  }
3979  else
3980  {
3981  if (m_generate_new.empty()) {
3982  fail_msg_writer() << tr("specify a wallet path with --generate-new-wallet (not --wallet-file)");
3983  return false;
3984  }
3985  m_wallet_file = m_generate_new;
3986  boost::optional<epee::wipeable_string> r;
3987  if (m_restore_multisig_wallet)
3988  r = new_wallet(vm, multisig_keys, old_language);
3989  else
3990  r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
3991  CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
3992  password = *r;
3993  welcome = true;
3994  }
3995 
3996  if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
3997  {
3998  m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) ||
3999  command_line::is_arg_defaulted(vm, arg_restore_date)));
4000  if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date))
4001  {
4002  uint16_t year;
4003  uint8_t month;
4004  uint8_t day;
4005  if (!datestr_to_int(m_restore_date, year, month, day))
4006  return false;
4007  try
4008  {
4009  m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
4010  success_msg_writer() << tr("Restore height is: ") << m_restore_height;
4011  }
4012  catch (const std::runtime_error& e)
4013  {
4014  fail_msg_writer() << e.what();
4015  return false;
4016  }
4017  }
4018  }
4019  if (!m_wallet->explicit_refresh_from_block_height() && m_restoring)
4020  {
4021  uint32_t version;
4022  bool connected = try_connect_to_daemon(false, &version);
4023  while (true)
4024  {
4025  std::string heightstr;
4026  if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
4027  heightstr = input_line("Restore from specific blockchain height (optional, default 0)");
4028  else
4029  heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD)");
4030  if (std::cin.eof())
4031  return false;
4032  if (heightstr.empty())
4033  {
4034  m_restore_height = 0;
4035  break;
4036  }
4037  try
4038  {
4039  m_restore_height = boost::lexical_cast<uint64_t>(heightstr);
4040  break;
4041  }
4042  catch (const boost::bad_lexical_cast &)
4043  {
4044  if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
4045  {
4046  fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr;
4047  continue;
4048  }
4049  uint16_t year;
4050  uint8_t month; // 1, 2, ..., 12
4051  uint8_t day; // 1, 2, ..., 31
4052  try
4053  {
4054  if (!datestr_to_int(heightstr, year, month, day))
4055  return false;
4056  m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
4057  success_msg_writer() << tr("Restore height is: ") << m_restore_height;
4058  std::string confirm = input_line(tr("Is this okay?"), true);
4059  if (std::cin.eof())
4060  return false;
4061  if(command_line::is_yes(confirm))
4062  break;
4063  }
4064  catch (const boost::bad_lexical_cast &)
4065  {
4066  fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr;
4067  }
4068  catch (const std::runtime_error& e)
4069  {
4070  fail_msg_writer() << e.what();
4071  }
4072  }
4073  }
4074  }
4075  if (m_restoring)
4076  {
4077  uint64_t estimate_height = m_wallet->estimate_blockchain_height();
4078  if (m_restore_height >= estimate_height)
4079  {
4080  success_msg_writer() << tr("Restore height ") << m_restore_height << (" is not yet reached. The current estimated height is ") << estimate_height;
4081  std::string confirm = input_line(tr("Still apply restore height?"), true);
4082  if (std::cin.eof() || command_line::is_no(confirm))
4083  m_restore_height = 0;
4084  }
4085  m_wallet->set_refresh_from_block_height(m_restore_height);
4086  }
4087  m_wallet->rewrite(m_wallet_file, password);
4088  }
4089  else // OPENING A WALLET FROM A WALLET FILE
4090  {
4091  assert(!m_wallet_file.empty());
4092  if (!m_subaddress_lookahead.empty())
4093  {
4094  fail_msg_writer() << tr("can't specify --subaddress-lookahead and --wallet-file at the same time");
4095  return false;
4096  }
4097  auto r = open_wallet(vm);
4098  CHECK_AND_ASSERT_MES(r, false, tr("failed to open account"));
4099  password = *r;
4100  }
4101  if (!m_wallet)
4102  {
4103  fail_msg_writer() << tr("wallet is null");
4104  return false;
4105  }
4106 
4107  if (!m_wallet->is_trusted_daemon())
4108  message_writer() << (boost::format(tr("Warning: using an untrusted daemon at %s, privacy will be lessened")) % m_wallet->get_daemon_address()).str();
4109 
4110  if (m_wallet->get_ring_database().empty())
4111  fail_msg_writer() << tr("Failed to initialize ring database: privacy enhancing features will be inactive");
4112 
4113  m_wallet->callback(this);
4114 
4115  check_background_mining(password);
4116 
4117  if (welcome)
4118  message_writer(console_color_yellow, true) << tr("If you are new to Electroneum, type \"welcome\" for a brief overview.");
4119 
4120  /* Comment out this warning message for now
4121  if (m_long_payment_id_support)
4122  {
4123  message_writer(console_color_red, false) <<
4124  tr("WARNING: obsolete long payment IDs are enabled. Sending transactions with those payment IDs are bad for your privacy.");
4125  message_writer(console_color_red, false) <<
4126  tr("It is recommended that you do not use them, and ask recipients who ask for one to not endanger your privacy.");
4127  }
4128  */
4129 
4130  return true;
4131 }
4132 //----------------------------------------------------------------------------------------------------
4134 {
4135  if (!m_wallet.get())
4136  return true;
4137 
4138  return close_wallet();
4139 }
4140 //----------------------------------------------------------------------------------------------------
4141 bool simple_wallet::handle_command_line(const boost::program_options::variables_map& vm)
4142 {
4143  m_wallet_file = command_line::get_arg(vm, arg_wallet_file);
4144  m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
4145  m_generate_from_device = command_line::get_arg(vm, arg_generate_from_device);
4146  m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key);
4147  m_generate_from_spend_key = command_line::get_arg(vm, arg_generate_from_spend_key);
4148  m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys);
4149  m_generate_from_multisig_keys = command_line::get_arg(vm, arg_generate_from_multisig_keys);
4150  m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json);
4151  m_mnemonic_language = command_line::get_arg(vm, arg_mnemonic_language);
4152  m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
4153  m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
4154  m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
4155  m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
4156  m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
4157  m_restore_height = command_line::get_arg(vm, arg_restore_height);
4158  m_restore_date = command_line::get_arg(vm, arg_restore_date);
4159  m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay);
4160  m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead);
4161  m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names);
4162  m_long_payment_id_support = true; //Hard code long payment id support to TRUE for now //command_line::get_arg(vm, arg_long_payment_id_support);
4163  m_restoring = !m_generate_from_view_key.empty() ||
4164  !m_generate_from_spend_key.empty() ||
4165  !m_generate_from_keys.empty() ||
4166  !m_generate_from_multisig_keys.empty() ||
4167  !m_generate_from_json.empty() ||
4168  !m_generate_from_device.empty() ||
4169  m_restore_deterministic_wallet ||
4170  m_restore_multisig_wallet;
4171  m_account_major_offset = command_line::get_arg(vm, arg_account_major_offset);
4172 
4173  if (!command_line::is_arg_defaulted(vm, arg_restore_date))
4174  {
4175  uint16_t year;
4176  uint8_t month, day;
4177  if (!datestr_to_int(m_restore_date, year, month, day))
4178  return false;
4179  }
4180 
4181  return true;
4182 }
4183 //----------------------------------------------------------------------------------------------------
4184 bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
4185 {
4186  uint32_t version_ = 0;
4187  if (!version)
4188  version = &version_;
4189  if (!m_wallet->check_connection(version))
4190  {
4191  if (!silent)
4192  fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " <<
4193  tr("Daemon either is not started or wrong port was passed. "
4194  "Please make sure daemon is running or change the daemon address using the 'set_daemon' command.");
4195  return false;
4196  }
4197  if (!m_allow_mismatched_daemon_version && ((*version >> 16) != CORE_RPC_VERSION_MAJOR))
4198  {
4199  if (!silent)
4200  fail_msg_writer() << boost::format(tr("Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version.")) % (*version>>16) % CORE_RPC_VERSION_MAJOR % m_wallet->get_daemon_address();
4201  return false;
4202  }
4203  return true;
4204 }
4205 
4213 std::string simple_wallet::get_mnemonic_language()
4214 {
4215  std::vector<std::string> language_list_self, language_list_english;
4216  const std::vector<std::string> &language_list = m_use_english_language_names ? language_list_english : language_list_self;
4217  std::string language_choice;
4218  int language_number = -1;
4219  crypto::ElectrumWords::get_language_list(language_list_self, false);
4220  crypto::ElectrumWords::get_language_list(language_list_english, true);
4221  std::cout << tr("List of available languages for your wallet's seed:") << std::endl;
4222  std::cout << tr("If your display freezes, exit blind with ^C, then run again with --use-english-language-names") << std::endl;
4223  int ii;
4224  std::vector<std::string>::const_iterator it;
4225  for (it = language_list.begin(), ii = 0; it != language_list.end(); it++, ii++)
4226  {
4227  std::cout << ii << " : " << *it << std::endl;
4228  }
4229  while (language_number < 0)
4230  {
4231  language_choice = input_line(tr("Enter the number corresponding to the language of your choice"));
4232  if (std::cin.eof())
4233  return std::string();
4234  try
4235  {
4236  language_number = std::stoi(language_choice);
4237  if (!((language_number >= 0) && (static_cast<unsigned int>(language_number) < language_list.size())))
4238  {
4239  language_number = -1;
4240  fail_msg_writer() << tr("invalid language choice entered. Please try again.\n");
4241  }
4242  }
4243  catch (const std::exception &e)
4244  {
4245  fail_msg_writer() << tr("invalid language choice entered. Please try again.\n");
4246  }
4247  }
4248  return language_list_self[language_number];
4249 }
4250 //----------------------------------------------------------------------------------------------------
4251 boost::optional<tools::password_container> simple_wallet::get_and_verify_password() const
4252 {
4253  auto pwd_container = default_password_prompter(m_wallet_file.empty());
4254  if (!pwd_container)
4255  return boost::none;
4256 
4257  if (!m_wallet->verify_password(pwd_container->password()))
4258  {
4259  fail_msg_writer() << tr("invalid password");
4260  return boost::none;
4261  }
4262  return pwd_container;
4263 }
4264 //----------------------------------------------------------------------------------------------------
4265 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
4266  const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
4267 {
4268  auto rc = tools::wallet2::make_new(vm, false, password_prompter);
4269  m_wallet = std::move(rc.first);
4270  if (!m_wallet)
4271  {
4272  return {};
4273  }
4274  epee::wipeable_string password = rc.second.password();
4275 
4276  if (!m_subaddress_lookahead.empty())
4277  {
4278  auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead);
4279  assert(lookahead);
4280  m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
4281  }
4282 
4283  bool was_deprecated_wallet = m_restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
4285 
4286  std::string mnemonic_language = old_language;
4287 
4288  std::vector<std::string> language_list;
4290  if (mnemonic_language.empty() && std::find(language_list.begin(), language_list.end(), m_mnemonic_language) != language_list.end())
4291  {
4292  mnemonic_language = m_mnemonic_language;
4293  }
4294 
4295  // Ask for seed language if:
4296  // it's a deterministic wallet AND
4297  // a seed language is not already specified AND
4298  // (it is not a wallet restore OR if it was a deprecated wallet
4299  // that was earlier used before this restore)
4300  if ((!two_random) && (mnemonic_language.empty() || mnemonic_language == crypto::ElectrumWords::old_language_name) && (!m_restore_deterministic_wallet || was_deprecated_wallet))
4301  {
4302  if (was_deprecated_wallet)
4303  {
4304  // The user had used an older version of the wallet with old style mnemonics.
4305  message_writer(console_color_green, false) << "\n" << tr("You had been using "
4306  "a deprecated version of the wallet. Please use the new seed that we provide.\n");
4307  }
4308  mnemonic_language = get_mnemonic_language();
4309  if (mnemonic_language.empty())
4310  return {};
4311  }
4312 
4313  m_wallet->set_seed_language(mnemonic_language);
4314 
4315  bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
4316 
4317  crypto::secret_key recovery_val;
4318  try
4319  {
4320  recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
4321  message_writer(console_color_white, true) << tr("Generated new wallet: ")
4322  << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4323  PAUSE_READLINE();
4324  std::cout << tr("View key: ");
4325  print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
4326  putchar('\n');
4327  }
4328  catch (const std::exception& e)
4329  {
4330  fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
4331  return {};
4332  }
4333 
4334  // convert rng value to electrum-style word list
4335  epee::wipeable_string electrum_words;
4336 
4337  crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language);
4338 
4339  success_msg_writer() <<
4340  "**********************************************************************\n" <<
4341  tr("Your wallet has been generated!\n"
4342  "To start synchronizing with the daemon, use the \"refresh\" command.\n"
4343  "Use the \"help\" command to see the list of available commands.\n"
4344  "Use \"help <command>\" to see a command's documentation.\n"
4345  "Always use the \"exit\" command when closing electroneum-wallet-cli to save \n"
4346  "your current session's state. Otherwise, you might need to synchronize \n"
4347  "your wallet again (your wallet keys are NOT at risk in any case).\n")
4348  ;
4349 
4350  if (!two_random)
4351  {
4352  print_seed(electrum_words);
4353  }
4354  success_msg_writer() << "**********************************************************************";
4355 
4356  return std::move(password);
4357 }
4358 //----------------------------------------------------------------------------------------------------
4359 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
4360  const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
4361  const crypto::secret_key& viewkey)
4362 {
4363  auto rc = tools::wallet2::make_new(vm, false, password_prompter);
4364  m_wallet = std::move(rc.first);
4365  if (!m_wallet)
4366  {
4367  return {};
4368  }
4369  epee::wipeable_string password = rc.second.password();
4370 
4371  if (!m_subaddress_lookahead.empty())
4372  {
4373  auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead);
4374  assert(lookahead);
4375  m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
4376  }
4377 
4378  if (m_restore_height)
4379  m_wallet->set_refresh_from_block_height(m_restore_height);
4380 
4381  if (m_account_major_offset)
4382  m_wallet->account_major_offset(m_account_major_offset);
4383 
4384  bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
4385 
4386  try
4387  {
4388  if (spendkey)
4389  {
4390  m_wallet->generate(m_wallet_file, std::move(rc.second).password(), address, *spendkey, viewkey, create_address_file);
4391  }
4392  else
4393  {
4394  m_wallet->generate(m_wallet_file, std::move(rc.second).password(), address, viewkey, create_address_file);
4395  }
4396  message_writer(console_color_white, true) << tr("Generated new wallet: ")
4397  << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4398  }
4399  catch (const std::exception& e)
4400  {
4401  fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
4402  return {};
4403  }
4404 
4405 
4406  return std::move(password);
4407 }
4408 
4409 //----------------------------------------------------------------------------------------------------
4410 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm)
4411 {
4412  auto rc = tools::wallet2::make_new(vm, false, password_prompter);
4413  m_wallet = std::move(rc.first);
4414  m_wallet->callback(this);
4415  if (!m_wallet)
4416  {
4417  return {};
4418  }
4419  epee::wipeable_string password = rc.second.password();
4420 
4421  if (!m_subaddress_lookahead.empty())
4422  {
4423  auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead);
4424  assert(lookahead);
4425  m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
4426  }
4427 
4428  if (m_restore_height)
4429  m_wallet->set_refresh_from_block_height(m_restore_height);
4430 
4431  auto device_desc = tools::wallet2::device_name_option(vm);
4432  auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm);
4433  try
4434  {
4435  bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
4436  m_wallet->device_derivation_path(device_derivation_path);
4437  m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
4438  message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
4439  << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4440  }
4441  catch (const std::exception& e)
4442  {
4443  fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
4444  return {};
4445  }
4446 
4447  return std::move(password);
4448 }
4449 //----------------------------------------------------------------------------------------------------
4450 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
4451  const epee::wipeable_string &multisig_keys, const std::string &old_language)
4452 {
4453  auto rc = tools::wallet2::make_new(vm, false, password_prompter);
4454  m_wallet = std::move(rc.first);
4455  if (!m_wallet)
4456  {
4457  return {};
4458  }
4459  epee::wipeable_string password = rc.second.password();
4460 
4461  if (!m_subaddress_lookahead.empty())
4462  {
4463  auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead);
4464  assert(lookahead);
4465  m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
4466  }
4467 
4468  std::string mnemonic_language = old_language;
4469 
4470  std::vector<std::string> language_list;
4472  if (mnemonic_language.empty() && std::find(language_list.begin(), language_list.end(), m_mnemonic_language) != language_list.end())
4473  {
4474  mnemonic_language = m_mnemonic_language;
4475  }
4476 
4477  m_wallet->set_seed_language(mnemonic_language);
4478 
4479  bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
4480 
4481  try
4482  {
4483  m_wallet->generate(m_wallet_file, std::move(rc.second).password(), multisig_keys, create_address_file);
4484  bool ready;
4485  uint32_t threshold, total;
4486  if (!m_wallet->multisig(&ready, &threshold, &total) || !ready)
4487  {
4488  fail_msg_writer() << tr("failed to generate new mutlisig wallet");
4489  return {};
4490  }
4491  message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total
4492  << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4493  }
4494  catch (const std::exception& e)
4495  {
4496  fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
4497  return {};
4498  }
4499 
4500  return std::move(password);
4501 }
4502 //----------------------------------------------------------------------------------------------------
4503 boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
4504 {
4505  if (!tools::wallet2::wallet_valid_path_format(m_wallet_file))
4506  {
4507  fail_msg_writer() << tr("wallet file path not valid: ") << m_wallet_file;
4508  return {};
4509  }
4510 
4511  bool keys_file_exists;
4512  bool wallet_file_exists;
4513 
4514  tools::wallet2::wallet_exists(m_wallet_file, keys_file_exists, wallet_file_exists);
4515  if(!keys_file_exists)
4516  {
4517  fail_msg_writer() << tr("Key file not found. Failed to open wallet");
4518  return {};
4519  }
4520 
4521  epee::wipeable_string password;
4522  try
4523  {
4524  auto rc = tools::wallet2::make_from_file(vm, false, "", password_prompter);
4525  m_wallet = std::move(rc.first);
4526  password = std::move(std::move(rc.second).password());
4527  if (!m_wallet)
4528  {
4529  return {};
4530  }
4531 
4532  m_wallet->callback(this);
4533  m_wallet->load(m_wallet_file, password);
4534  std::string prefix;
4535  bool ready;
4536  uint32_t threshold, total;
4537  if (m_wallet->watch_only())
4538  prefix = tr("Opened watch-only wallet");
4539  else if (m_wallet->multisig(&ready, &threshold, &total))
4540  prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str();
4541  else
4542  prefix = tr("Opened wallet");
4543  message_writer(console_color_white, true) <<
4544  prefix << ": " << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4545  if (m_wallet->get_account().get_device()) {
4546  message_writer(console_color_white, true) << "Wallet is on device: " << m_wallet->get_account().get_device().get_name();
4547  }
4548  // If the wallet file is deprecated, we should ask for mnemonic language again and store
4549  // everything in the new format.
4550  // NOTE: this is_deprecated() refers to the wallet file format before becoming JSON. It does not refer to the "old english" seed words form of "deprecated" used elsewhere.
4551  if (m_wallet->is_deprecated())
4552  {
4553  bool is_deterministic;
4554  {
4556  is_deterministic = m_wallet->is_deterministic();
4557  }
4558  if (is_deterministic)
4559  {
4560  message_writer(console_color_green, false) << "\n" << tr("You had been using "
4561  "a deprecated version of the wallet. Please proceed to upgrade your wallet.\n");
4562  std::string mnemonic_language = get_mnemonic_language();
4563  if (mnemonic_language.empty())
4564  return {};
4565  m_wallet->set_seed_language(mnemonic_language);
4566  m_wallet->rewrite(m_wallet_file, password);
4567 
4568  // Display the seed
4569  epee::wipeable_string seed;
4570  m_wallet->get_seed(seed);
4571  print_seed(seed);
4572  }
4573  else
4574  {
4575  message_writer(console_color_green, false) << "\n" << tr("You had been using "
4576  "a deprecated version of the wallet. Your wallet file format is being upgraded now.\n");
4577  m_wallet->rewrite(m_wallet_file, password);
4578  }
4579  }
4580  }
4581  catch (const std::exception& e)
4582  {
4583  fail_msg_writer() << tr("failed to load wallet: ") << e.what();
4584  if (m_wallet)
4585  {
4586  // only suggest removing cache if the password was actually correct
4587  bool password_is_correct = false;
4588  try
4589  {
4590  password_is_correct = m_wallet->verify_password(password);
4591  }
4592  catch (...) { } // guard against I/O errors
4593  if (password_is_correct)
4594  fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file;
4595  }
4596  return {};
4597  }
4598  success_msg_writer() <<
4599  "**********************************************************************\n" <<
4600  tr("Use the \"help\" command to see the list of available commands.\n") <<
4601  tr("Use \"help <command>\" to see a command's documentation.\n") <<
4602  "**********************************************************************";
4603  return std::move(password);
4604 }
4605 //----------------------------------------------------------------------------------------------------
4606 bool simple_wallet::close_wallet()
4607 {
4608  if (m_idle_run.load(std::memory_order_relaxed))
4609  {
4610  m_idle_run.store(false, std::memory_order_relaxed);
4611  m_wallet->stop();
4612  {
4613  boost::unique_lock<boost::mutex> lock(m_idle_mutex);
4614  m_idle_cond.notify_one();
4615  }
4616  m_idle_thread.join();
4617  }
4618 
4619  bool r = m_wallet->deinit();
4620  if (!r)
4621  {
4622  fail_msg_writer() << tr("failed to deinitialize wallet");
4623  return false;
4624  }
4625 
4626  try
4627  {
4628  m_wallet->store();
4629  }
4630  catch (const std::exception& e)
4631  {
4632  fail_msg_writer() << e.what();
4633  return false;
4634  }
4635 
4636  return true;
4637 }
4638 //----------------------------------------------------------------------------------------------------
4639 bool simple_wallet::save(const std::vector<std::string> &args)
4640 {
4641  try
4642  {
4643  LOCK_IDLE_SCOPE();
4644  m_wallet->store();
4645  success_msg_writer() << tr("Wallet data saved");
4646  }
4647  catch (const std::exception& e)
4648  {
4649  fail_msg_writer() << e.what();
4650  }
4651 
4652  return true;
4653 }
4654 //----------------------------------------------------------------------------------------------------
4655 bool simple_wallet::save_watch_only(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
4656 {
4657  if (m_wallet->multisig())
4658  {
4659  fail_msg_writer() << tr("wallet is multisig and cannot save a watch-only version");
4660  return true;
4661  }
4662 
4663  const auto pwd_container = password_prompter(tr("Password for new watch-only wallet"), true);
4664 
4665  if (!pwd_container)
4666  {
4667  fail_msg_writer() << tr("failed to read wallet password");
4668  return true;
4669  }
4670 
4671  try
4672  {
4673  std::string new_keys_filename;
4674  m_wallet->write_watch_only_wallet(m_wallet_file, pwd_container->password(), new_keys_filename);
4675  success_msg_writer() << tr("Watch only wallet saved as: ") << new_keys_filename;
4676  }
4677  catch (const std::exception &e)
4678  {
4679  fail_msg_writer() << tr("Failed to save watch only wallet: ") << e.what();
4680  return true;
4681  }
4682  return true;
4683 }
4684 //----------------------------------------------------------------------------------------------------
4685 void simple_wallet::start_background_mining()
4686 {
4689  bool r = m_wallet->invoke_http_json("/mining_status", reqq, resq);
4690  std::string err = interpret_rpc_response(r, resq.status);
4691  if (!r)
4692  return;
4693  if (!err.empty())
4694  {
4695  fail_msg_writer() << tr("Failed to query mining status: ") << err;
4696  return;
4697  }
4698  if (!resq.is_background_mining_enabled)
4699  {
4702  req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4703  req.threads_count = 1;
4704  req.do_background_mining = true;
4705  req.ignore_battery = false;
4706  bool r = m_wallet->invoke_http_json("/start_mining", req, res);
4707  std::string err = interpret_rpc_response(r, res.status);
4708  if (!err.empty())
4709  {
4710  fail_msg_writer() << tr("Failed to setup background mining: ") << err;
4711  return;
4712  }
4713  }
4714  success_msg_writer() << tr("Background mining enabled. Thank you for supporting the Electroneum network.");
4715 }
4716 //----------------------------------------------------------------------------------------------------
4717 void simple_wallet::stop_background_mining()
4718 {
4721  bool r = m_wallet->invoke_http_json("/mining_status", reqq, resq);
4722  if (!r)
4723  return;
4724  std::string err = interpret_rpc_response(r, resq.status);
4725  if (!err.empty())
4726  {
4727  fail_msg_writer() << tr("Failed to query mining status: ") << err;
4728  return;
4729  }
4730  if (resq.is_background_mining_enabled)
4731  {
4734  bool r = m_wallet->invoke_http_json("/stop_mining", req, res);
4735  std::string err = interpret_rpc_response(r, res.status);
4736  if (!err.empty())
4737  {
4738  fail_msg_writer() << tr("Failed to setup background mining: ") << err;
4739  return;
4740  }
4741  }
4742  //message_writer(console_color_red, false) << tr("Background mining not enabled. Run \"set setup-background-mining 1\" to change.");
4743 }
4744 //----------------------------------------------------------------------------------------------------
4745 void simple_wallet::check_background_mining(const epee::wipeable_string &password)
4746 {
4747  tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining();
4749  {
4750  //message_writer(console_color_red, false) << tr("Background mining not enabled. Run \"set setup-background-mining 1\" to change.");
4751  return;
4752  }
4753 
4754  if (!m_wallet->is_trusted_daemon())
4755  {
4756  //message_writer() << tr("Using an untrusted daemon, skipping background mining check");
4757  return;
4758  }
4759 
4762  bool r = m_wallet->invoke_http_json("/mining_status", req, res);
4763  std::string err = interpret_rpc_response(r, res.status);
4764  bool is_background_mining_enabled = false;
4765  if (err.empty())
4766  is_background_mining_enabled = res.is_background_mining_enabled;
4767 
4768  if (is_background_mining_enabled)
4769  {
4770  // already active, nice
4771  m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes);
4772  m_wallet->rewrite(m_wallet_file, password);
4773  start_background_mining();
4774  return;
4775  }
4776  if (res.active)
4777  return;
4778 
4780  {
4781  //message_writer() << tr("The daemon is not set up to background mine.");
4782  //message_writer() << tr("With background mining enabled, the daemon will mine when idle and not on batttery.");
4783  //message_writer() << tr("Enabling this supports the network you are using, and makes you eligible for receiving new Electroneum");
4784  //std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): "));
4785 
4786  return;
4787 
4788  /*
4789  if (std::cin.eof() || !command_line::is_yes(accepted)) {
4790  m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningNo);
4791  m_wallet->rewrite(m_wallet_file, password);
4792  message_writer(console_color_red, false) << tr("Background mining not enabled. Set setup-background-mining to 1 to change.");
4793  return;
4794  }
4795  m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes);
4796  m_wallet->rewrite(m_wallet_file, password);
4797  start_background_mining();
4798  */
4799  }
4800 }
4801 //----------------------------------------------------------------------------------------------------
4802 bool simple_wallet::start_mining(const std::vector<std::string>& args)
4803 {
4804  if (!m_wallet->is_trusted_daemon())
4805  {
4806  fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
4807  return true;
4808  }
4809 
4810  if (!try_connect_to_daemon())
4811  return true;
4812 
4813  if (!m_wallet)
4814  {
4815  fail_msg_writer() << tr("wallet is null");
4816  return true;
4817  }
4819  req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4820 
4821  bool ok = true;
4822  size_t arg_size = args.size();
4823  if(arg_size >= 3)
4824  {
4825  if (!parse_bool_and_use(args[2], [&](bool r) { req.ignore_battery = r; }))
4826  return true;
4827  }
4828  if(arg_size >= 2)
4829  {
4830  if (!parse_bool_and_use(args[1], [&](bool r) { req.do_background_mining = r; }))
4831  return true;
4832  }
4833  if(arg_size >= 1)
4834  {
4835  uint16_t num = 1;
4836  ok = string_tools::get_xtype_from_string(num, args[0]);
4837  ok = ok && 1 <= num;
4838  req.threads_count = num;
4839  }
4840  else
4841  {
4842  req.threads_count = 1;
4843  }
4844 
4845  if (!ok)
4846  {
4847  PRINT_USAGE(USAGE_START_MINING);
4848  return true;
4849  }
4850 
4852  bool r = m_wallet->invoke_http_json("/start_mining", req, res);
4853  std::string err = interpret_rpc_response(r, res.status);
4854  if (err.empty())
4855  success_msg_writer() << tr("Mining started in daemon");
4856  else
4857  fail_msg_writer() << tr("mining has NOT been started: ") << err;
4858  return true;
4859 }
4860 //----------------------------------------------------------------------------------------------------
4861 bool simple_wallet::stop_mining(const std::vector<std::string>& args)
4862 {
4863  if (!try_connect_to_daemon())
4864  return true;
4865 
4866  if (!m_wallet)
4867  {
4868  fail_msg_writer() << tr("wallet is null");
4869  return true;
4870  }
4871 
4874  bool r = m_wallet->invoke_http_json("/stop_mining", req, res);
4875  std::string err = interpret_rpc_response(r, res.status);
4876  if (err.empty())
4877  success_msg_writer() << tr("Mining stopped in daemon");
4878  else
4879  fail_msg_writer() << tr("mining has NOT been stopped: ") << err;
4880  return true;
4881 }
4882 //----------------------------------------------------------------------------------------------------
4883 bool simple_wallet::set_daemon(const std::vector<std::string>& args)
4884 {
4885  std::string daemon_url;
4886 
4887  if (args.size() < 1)
4888  {
4889  PRINT_USAGE(USAGE_SET_DAEMON);
4890  return true;
4891  }
4892 
4893  boost::regex rgx("^(.*://)?([A-Za-z0-9\\-\\.]+)(:[0-9]+)?");
4894  boost::cmatch match;
4895  // If user input matches URL regex
4896  if (boost::regex_match(args[0].c_str(), match, rgx))
4897  {
4898  if (match.length() < 4)
4899  {
4900  fail_msg_writer() << tr("Unexpected array length - Exited simple_wallet::set_daemon()");
4901  return true;
4902  }
4903  // If no port has been provided, use the default from config
4904  if (!match[3].length())
4905  {
4906  int daemon_port = get_config(m_wallet->nettype()).RPC_DEFAULT_PORT;
4907  daemon_url = match[1] + match[2] + std::string(":") + std::to_string(daemon_port);
4908  } else {
4909  daemon_url = args[0];
4910  }
4911  LOCK_IDLE_SCOPE();
4912  m_wallet->init(daemon_url);
4913 
4914  if (args.size() == 2)
4915  {
4916  if (args[1] == "trusted")
4917  m_wallet->set_trusted_daemon(true);
4918  else if (args[1] == "untrusted")
4919  m_wallet->set_trusted_daemon(false);
4920  else
4921  {
4922  fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted";
4923  m_wallet->set_trusted_daemon(false);
4924  }
4925  }
4926  else
4927  {
4928  m_wallet->set_trusted_daemon(false);
4929  try
4930  {
4931  if (tools::is_local_address(m_wallet->get_daemon_address()))
4932  {
4933  MINFO(tr("Daemon is local, assuming trusted"));
4934  m_wallet->set_trusted_daemon(true);
4935  }
4936  }
4937  catch (const std::exception &e) { }
4938  }
4939  success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ? tr("trusted") : tr("untrusted"));
4940  } else {
4941  fail_msg_writer() << tr("This does not seem to be a valid daemon URL.");
4942  }
4943  return true;
4944 }
4945 //----------------------------------------------------------------------------------------------------
4946 bool simple_wallet::save_bc(const std::vector<std::string>& args)
4947 {
4948  if (!try_connect_to_daemon())
4949  return true;
4950 
4951  if (!m_wallet)
4952  {
4953  fail_msg_writer() << tr("wallet is null");
4954  return true;
4955  }
4958  bool r = m_wallet->invoke_http_json("/save_bc", req, res);
4959  std::string err = interpret_rpc_response(r, res.status);
4960  if (err.empty())
4961  success_msg_writer() << tr("Blockchain saved");
4962  else
4963  fail_msg_writer() << tr("blockchain can't be saved: ") << err;
4964  return true;
4965 }
4966 //----------------------------------------------------------------------------------------------------
4967 void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block)
4968 {
4969  if (!m_auto_refresh_refreshing)
4970  m_refresh_progress_reporter.update(height, false);
4971 }
4972 //----------------------------------------------------------------------------------------------------
4973 void simple_wallet::on_etn_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time)
4974 {
4975  message_writer(console_color_green, false) << "\r" <<
4976  tr("Height ") << height << ", " <<
4977  tr("txid ") << txid << ", " <<
4978  print_etn(amount) << ", " <<
4979  tr("idx ") << subaddr_index;
4980 
4981  const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == STAGENET ? 50000 : 1650000;
4982  if (height >= warn_height)
4983  {
4984  std::vector<tx_extra_field> tx_extra_fields;
4985  parse_tx_extra(tx.extra, tx_extra_fields); // failure ok
4986  tx_extra_nonce extra_nonce;
4987  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
4988  {
4989  crypto::hash payment_id = crypto::null_hash;
4990  if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
4991  message_writer(console_color_red, false) <<
4992  (m_long_payment_id_support ? tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead.") : tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete. Support will be withdrawn in the future. Use subaddresses instead."));
4993  }
4994  }
4995  if (unlock_time)
4996  message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid);
4997  if (m_auto_refresh_refreshing)
4998  m_cmd_binder.print_prompt();
4999  else
5000  m_refresh_progress_reporter.update(height, true);
5001 }
5002 //----------------------------------------------------------------------------------------------------
5003 void simple_wallet::on_unconfirmed_etn_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index)
5004 {
5005  // Not implemented in CLI wallet
5006 }
5007 //----------------------------------------------------------------------------------------------------
5008 void simple_wallet::on_etn_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index)
5009 {
5010  message_writer(console_color_magenta, false) << "\r" <<
5011  tr("Height ") << height << ", " <<
5012  tr("txid ") << txid << ", " <<
5013  tr("spent ") << print_etn(amount) << ", " <<
5014  tr("idx ") << subaddr_index;
5015  if (m_auto_refresh_refreshing)
5016  m_cmd_binder.print_prompt();
5017  else
5018  m_refresh_progress_reporter.update(height, true);
5019 }
5020 //----------------------------------------------------------------------------------------------------
5021 void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx)
5022 {
5023 }
5024 //----------------------------------------------------------------------------------------------------
5025 boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char *reason)
5026 {
5027  // can't ask for password from a background thread
5028  if (!m_in_manual_refresh.load(std::memory_order_relaxed))
5029  {
5030  message_writer(console_color_red, false) << boost::format(tr("Password needed (%s) - use the refresh command")) % reason;
5031  m_cmd_binder.print_prompt();
5032  return boost::none;
5033  }
5034 
5035 #ifdef HAVE_READLINE
5036  rdln::suspend_readline pause_readline;
5037 #endif
5038  std::string msg = tr("Enter password");
5039  if (reason && *reason)
5040  msg += std::string(" (") + reason + ")";
5041  auto pwd_container = tools::password_container::prompt(false, msg.c_str());
5042  if (!pwd_container)
5043  {
5044  MERROR("Failed to read password");
5045  return boost::none;
5046  }
5047 
5048  return pwd_container->password();
5049 }
5050 //----------------------------------------------------------------------------------------------------
5051 void simple_wallet::on_device_button_request(uint64_t code)
5052 {
5053  message_writer(console_color_white, false) << tr("Device requires attention");
5054 }
5055 //----------------------------------------------------------------------------------------------------
5056 boost::optional<epee::wipeable_string> simple_wallet::on_device_pin_request()
5057 {
5058 #ifdef HAVE_READLINE
5059  rdln::suspend_readline pause_readline;
5060 #endif
5061  std::string msg = tr("Enter device PIN");
5062  auto pwd_container = tools::password_container::prompt(false, msg.c_str());
5063  THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN"));
5064  return pwd_container->password();
5065 }
5066 //----------------------------------------------------------------------------------------------------
5067 boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_request(bool on_device)
5068 {
5069  if (on_device){
5070  message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device");
5071  return boost::none;
5072  }
5073 
5074 #ifdef HAVE_READLINE
5075  rdln::suspend_readline pause_readline;
5076 #endif
5077  std::string msg = tr("Enter device passphrase");
5078  auto pwd_container = tools::password_container::prompt(false, msg.c_str());
5079  THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase"));
5080  return pwd_container->password();
5081 }
5082 //----------------------------------------------------------------------------------------------------
5083 void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_etn)
5084 {
5085  // Key image sync after the first refresh
5086  if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) {
5087  return;
5088  }
5089 
5090  if (!received_etn || m_wallet->get_device_last_key_image_sync() != 0) {
5091  return;
5092  }
5093 
5094  // Finished first refresh for HW device and etn received -> KI sync
5095  message_writer() << "\n" << tr("The first refresh has finished for the HW-based wallet with received ETN. hw_key_images_sync is needed. ");
5096 
5097  std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): "));
5098  if (std::cin.eof() || !command_line::is_yes(accepted)) {
5099  message_writer(console_color_red, false) << tr("hw_key_images_sync skipped. Run command manually before a transfer.");
5100  return;
5101  }
5102 
5103  key_images_sync_intern();
5104 }
5105 //----------------------------------------------------------------------------------------------------
5106 bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
5107 {
5108  if (!try_connect_to_daemon(is_init))
5109  return true;
5110 
5111  LOCK_IDLE_SCOPE();
5112 
5113  crypto::hash transfer_hash_pre{};
5114  uint64_t height_pre = 0, height_post;
5115  if (reset != ResetNone)
5116  {
5117  if (reset == ResetSoftKeepKI)
5118  height_pre = m_wallet->hash_m_transfers(-1, transfer_hash_pre);
5119 
5120  m_wallet->rescan_blockchain(reset == ResetHard, false, reset == ResetSoftKeepKI);
5121  }
5122 
5123 #ifdef HAVE_READLINE
5124  rdln::suspend_readline pause_readline;
5125 #endif
5126 
5127  message_writer() << tr("Starting refresh...");
5128 
5129  uint64_t fetched_blocks = 0;
5130  bool received_etn = false;
5131  bool ok = false;
5132  std::ostringstream ss;
5133  try
5134  {
5135  m_in_manual_refresh.store(true, std::memory_order_relaxed);
5136  epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
5137  m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_etn);
5138 
5139  if (reset == ResetSoftKeepKI)
5140  {
5141  m_wallet->finish_rescan_bc_keep_key_images(height_pre, transfer_hash_pre);
5142 
5143  height_post = m_wallet->get_num_transfer_details();
5144  if (height_pre != height_post)
5145  {
5146  message_writer() << tr("New transfer received since rescan was started. Key images are incomplete.");
5147  }
5148  }
5149 
5150  ok = true;
5151  // Clear line "Height xxx of xxx"
5152  std::cout << "\r \r";
5153  success_msg_writer(true) << tr("Refresh done, blocks received: ") << fetched_blocks;
5154  if (is_init)
5155  print_accounts();
5156  show_balance_unlocked();
5157  on_refresh_finished(start_height, fetched_blocks, is_init, received_etn);
5158  }
5159  catch (const tools::error::daemon_busy&)
5160  {
5161  ss << tr("daemon is busy. Please try again later.");
5162  }
5164  {
5165  ss << tr("no connection to daemon. Please make sure daemon is running.");
5166  }
5167  catch (const tools::error::wallet_rpc_error& e)
5168  {
5169  LOG_ERROR("RPC error: " << e.to_string());
5170  ss << tr("RPC error: ") << e.what();
5171  }
5172  catch (const tools::error::refresh_error& e)
5173  {
5174  LOG_ERROR("refresh error: " << e.to_string());
5175  ss << tr("refresh error: ") << e.what();
5176  }
5177  catch (const tools::error::wallet_internal_error& e)
5178  {
5179  LOG_ERROR("internal error: " << e.to_string());
5180  ss << tr("internal error: ") << e.what();
5181  }
5182  catch (const std::exception& e)
5183  {
5184  LOG_ERROR("unexpected error: " << e.what());
5185  ss << tr("unexpected error: ") << e.what();
5186  }
5187  catch (...)
5188  {
5189  LOG_ERROR("unknown error");
5190  ss << tr("unknown error");
5191  }
5192 
5193  if (!ok)
5194  {
5195  fail_msg_writer() << tr("refresh failed: ") << ss.str() << ". " << tr("Blocks received: ") << fetched_blocks;
5196  }
5197 
5198  return true;
5199 }
5200 //----------------------------------------------------------------------------------------------------
5201 bool simple_wallet::refresh(const std::vector<std::string>& args)
5202 {
5203  uint64_t start_height = 0;
5204  if(!args.empty()){
5205  try
5206  {
5207  start_height = boost::lexical_cast<uint64_t>( args[0] );
5208  }
5209  catch(const boost::bad_lexical_cast &)
5210  {
5211  start_height = 0;
5212  }
5213  }
5214  return refresh_main(start_height, ResetNone);
5215 }
5216 //----------------------------------------------------------------------------------------------------
5217 bool simple_wallet::show_balance_unlocked(bool detailed)
5218 {
5219  std::string extra;
5220  if (m_wallet->has_multisig_partial_key_images())
5221  extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)");
5222  else if (m_wallet->has_unknown_key_images())
5223  extra += tr(" (Some owned outputs have missing key images - import_key_images needed)");
5224 
5225  success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0});
5226  const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account];
5227  success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag);
5228 
5229  uint64_t blocks_to_unlock, blocks_to_unlock_public_chain;
5230  uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, false, &blocks_to_unlock);
5231  uint64_t unlocked_balance_public_chain = m_wallet->unlocked_balance(m_current_subaddress_account, true, &blocks_to_unlock_public_chain);
5232  std::string unlock_time_message, unlock_time_message_public_chain;
5233 
5234  if (blocks_to_unlock > 0) {
5235  unlock_time_message = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock).str();
5236  }
5237  if (blocks_to_unlock_public_chain > 0) {
5238  unlock_time_message_public_chain = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock_public_chain).str();
5239  }
5240 
5241  if (!m_wallet->synced_to_v10() || m_wallet->balance(m_current_subaddress_account, false)) {
5242  success_msg_writer() << tr("Pre V10 Balance: ") << print_etn(m_wallet->balance(m_current_subaddress_account, false))
5243  << ", "
5244  << tr("Pre V10 unlocked balance: ") << print_etn(unlocked_balance) << unlock_time_message << extra;
5245  }
5246  if (m_wallet->balance(m_current_subaddress_account, true)) {
5247  success_msg_writer() << tr("Balance: ") << print_etn(m_wallet->balance(m_current_subaddress_account, true))
5248  << ", "
5249  << tr("Unlocked balance: ") << print_etn(unlocked_balance_public_chain) << unlock_time_message_public_chain << extra;
5250  }
5251 
5252  std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account);
5253  std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account);
5254  std::map<uint32_t, uint64_t> balance_per_subaddress_public_chain = m_wallet->balance_per_subaddress(m_current_subaddress_account, true);
5255  std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress_public_chain = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account, true);
5256 
5257  if (!detailed || (balance_per_subaddress.empty() && balance_per_subaddress_public_chain.empty()))
5258  return true;
5259 
5260  if (!balance_per_subaddress.empty()) {
5261  success_msg_writer() << tr(" Pre V10 Balance per address:");
5263  << boost::format("%15s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") %
5264  tr("Outputs") % tr("Label");
5265  std::vector<tools::wallet2::transfer_details> transfers;
5266  m_wallet->get_transfers(transfers);
5267  for (const auto &i : balance_per_subaddress) {
5268  cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first};
5269  std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6);
5270  uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](
5272  return td.m_tx.version == 1 && !td.m_spent && td.m_subaddr_index == subaddr_index;
5273  });
5275  << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_etn(i.second) %
5276  print_etn(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs %
5277  m_wallet->get_subaddress_label(subaddr_index);
5278  }
5279  }
5280  if (!balance_per_subaddress_public_chain.empty()) {
5281  success_msg_writer() << tr("Balance per address:");
5283  << boost::format("%15s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") %
5284  tr("Outputs") % tr("Label");
5285  std::vector<tools::wallet2::transfer_details> transfers;
5286  m_wallet->get_transfers(transfers);
5287  for (const auto &i : balance_per_subaddress_public_chain) {
5288  cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first};
5289  std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6);
5290  uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](
5292  return td.m_tx.version > 1 && !td.m_spent && td.m_subaddr_index == subaddr_index;
5293  });
5295  << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_etn(i.second) %
5296  print_etn(unlocked_balance_per_subaddress_public_chain[i.first].first) % num_unspent_outputs %
5297  m_wallet->get_subaddress_label(subaddr_index);
5298  }
5299  }
5300 
5301  return true;
5302 }
5303 //----------------------------------------------------------------------------------------------------
5304 bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
5305 {
5306  if (args.size() > 1 || (args.size() == 1 && args[0] != "detail"))
5307  {
5308  PRINT_USAGE(USAGE_SHOW_BALANCE);
5309  return true;
5310  }
5311  LOCK_IDLE_SCOPE();
5312  show_balance_unlocked(args.size() == 1);
5313  return true;
5314 }
5315 //----------------------------------------------------------------------------------------------------
5316 bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args)
5317 {
5318  if (args.size() > 3)
5319  {
5320  PRINT_USAGE(USAGE_INCOMING_TRANSFERS);
5321  return true;
5322  }
5323  auto local_args = args;
5324  LOCK_IDLE_SCOPE();
5325 
5326  bool filter = false;
5327  bool available = false;
5328  bool verbose = false;
5329  bool uses = false;
5330  if (local_args.size() > 0)
5331  {
5332  if (local_args[0] == "available")
5333  {
5334  filter = true;
5335  available = true;
5336  local_args.erase(local_args.begin());
5337  }
5338  else if (local_args[0] == "unavailable")
5339  {
5340  filter = true;
5341  available = false;
5342  local_args.erase(local_args.begin());
5343  }
5344  }
5345  while (local_args.size() > 0)
5346  {
5347  if (local_args[0] == "verbose")
5348  verbose = true;
5349  else if (local_args[0] == "uses")
5350  uses = true;
5351  else
5352  {
5353  fail_msg_writer() << tr("Invalid keyword: ") << local_args.front();
5354  break;
5355  }
5356  local_args.erase(local_args.begin());
5357  }
5358 
5359  const uint64_t blockchain_height = m_wallet->get_blockchain_current_height();
5360 
5361  PAUSE_READLINE();
5362 
5363  std::set<uint32_t> subaddr_indices;
5364  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
5365  {
5366  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
5367  return true;
5368  local_args.erase(local_args.begin());
5369  }
5370 
5371  if (local_args.size() > 0)
5372  {
5373  PRINT_USAGE(USAGE_INCOMING_TRANSFERS);
5374  return true;
5375  }
5376 
5378  m_wallet->get_transfers(transfers);
5379 
5380  size_t transfers_found = 0;
5381  for (const auto& td : transfers)
5382  {
5383  if (!filter || available != td.m_spent)
5384  {
5385  if (m_current_subaddress_account != td.m_subaddr_index.major || (!subaddr_indices.empty() && subaddr_indices.count(td.m_subaddr_index.minor) == 0))
5386  continue;
5387  if (!transfers_found)
5388  {
5389  std::string verbose_string;
5390  if (verbose)
5391  verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str();
5392  message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string;
5393  }
5394  std::string extra_string;
5395  if (verbose)
5396  extra_string += (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str();
5397  if (uses)
5398  {
5399  std::vector<uint64_t> heights;
5400  for (const auto &e: td.m_uses) heights.push_back(e.first);
5401  const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height);
5402  extra_string += std::string("\n ") + tr("Used at heights: ") + line.first + "\n " + line.second;
5403  }
5404  message_writer(td.m_spent ? console_color_magenta : console_color_green, false) <<
5405  boost::format("%21s%8s%12s%8s%16u%68s%16u%s") %
5406  print_etn(td.amount()) %
5407  (td.m_spent ? tr("T") : tr("F")) %
5408  (m_wallet->frozen(td) ? tr("[frozen]") : m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) %
5409  (td.is_rct() ? tr("RingCT") : tr("-")) %
5410  td.m_global_output_index %
5411  td.m_txid %
5412  td.m_subaddr_index.minor %
5413  extra_string;
5414  ++transfers_found;
5415  }
5416  }
5417 
5418  if (!transfers_found)
5419  {
5420  if (!filter)
5421  {
5422  success_msg_writer() << tr("No incoming transfers");
5423  }
5424  else if (available)
5425  {
5426  success_msg_writer() << tr("No incoming available transfers");
5427  }
5428  else
5429  {
5430  success_msg_writer() << tr("No incoming unavailable transfers");
5431  }
5432  }
5433  else
5434  {
5435  success_msg_writer() << boost::format("Found %u/%u transfers") % transfers_found % transfers.size();
5436  }
5437 
5438  return true;
5439 }
5440 //----------------------------------------------------------------------------------------------------
5441 bool simple_wallet::show_payments(const std::vector<std::string> &args)
5442 {
5443  if(args.empty())
5444  {
5445  PRINT_USAGE(USAGE_PAYMENTS);
5446  return true;
5447  }
5448 
5449  LOCK_IDLE_SCOPE();
5450 
5451  PAUSE_READLINE();
5452 
5453  message_writer() << boost::format("%68s%68s%12s%21s%16s%16s") %
5454  tr("payment") % tr("transaction") % tr("height") % tr("amount") % tr("unlock time") % tr("addr index");
5455 
5456  bool payments_found = false;
5457  for(std::string arg : args)
5458  {
5459  crypto::hash payment_id;
5460  if(tools::wallet2::parse_payment_id(arg, payment_id))
5461  {
5462  std::list<tools::wallet2::payment_details> payments;
5463  m_wallet->get_payments(payment_id, payments);
5464  if(payments.empty())
5465  {
5466  success_msg_writer() << tr("No payments with id ") << payment_id;
5467  continue;
5468  }
5469 
5470  for (const tools::wallet2::payment_details& pd : payments)
5471  {
5472  if(!payments_found)
5473  {
5474  payments_found = true;
5475  }
5476  success_msg_writer(true) <<
5477  boost::format("%68s%68s%12s%21s%16s%16s") %
5478  payment_id %
5479  pd.m_tx_hash %
5480  pd.m_block_height %
5481  print_etn(pd.m_amount) %
5482  pd.m_unlock_time %
5483  pd.m_subaddr_index.minor;
5484  }
5485  }
5486  else
5487  {
5488  fail_msg_writer() << tr("payment ID has invalid format, expected 16 or 64 character hex string: ") << arg;
5489  }
5490  }
5491 
5492  return true;
5493 }
5494 //----------------------------------------------------------------------------------------------------
5495 uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err)
5496 {
5497  if (!m_wallet)
5498  {
5499  throw std::runtime_error("simple_wallet null wallet");
5500  }
5501  return m_wallet->get_daemon_blockchain_height(err);
5502 }
5503 //----------------------------------------------------------------------------------------------------
5504 bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
5505 {
5506  if (!try_connect_to_daemon())
5507  return true;
5508 
5509  std::string err;
5510  uint64_t bc_height = get_daemon_blockchain_height(err);
5511  if (err.empty())
5512  success_msg_writer() << bc_height;
5513  else
5514  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
5515  return true;
5516 }
5517 //----------------------------------------------------------------------------------------------------
5518 bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
5519 {
5520  if (!m_wallet->is_trusted_daemon())
5521  {
5522  fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
5523  return true;
5524  }
5525 
5526  if (!try_connect_to_daemon())
5527  return true;
5528 
5529  try
5530  {
5531  LOCK_IDLE_SCOPE();
5532  m_wallet->rescan_spent();
5533  }
5534  catch (const tools::error::daemon_busy&)
5535  {
5536  fail_msg_writer() << tr("daemon is busy. Please try again later.");
5537  }
5539  {
5540  fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
5541  }
5543  {
5544  fail_msg_writer() << tr("failed to get spent status for key image");
5545  }
5547  {
5548  fail_msg_writer() << tr("failed to get spent status for public output");
5549  }
5550  catch (const tools::error::wallet_rpc_error& e)
5551  {
5552  LOG_ERROR("RPC error: " << e.to_string());
5553  fail_msg_writer() << tr("RPC error: ") << e.what();
5554  }
5555  catch (const std::exception& e)
5556  {
5557  LOG_ERROR("unexpected error: " << e.what());
5558  fail_msg_writer() << tr("unexpected error: ") << e.what();
5559  }
5560  catch (...)
5561  {
5562  LOG_ERROR("unknown error");
5563  fail_msg_writer() << tr("unknown error");
5564  }
5565 
5566  return true;
5567 }
5568 //----------------------------------------------------------------------------------------------------
5569 std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height) const
5570 {
5571  std::stringstream ostr;
5572 
5573  for (uint64_t h: heights)
5574  blockchain_height = std::max(blockchain_height, h);
5575 
5576  for (size_t j = 0; j < heights.size(); ++j)
5577  ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j];
5578 
5579  // visualize the distribution, using the code by moneroexamples onion-monero-viewer
5580  const uint64_t resolution = 79;
5581  std::string ring_str(resolution, '_');
5582  for (size_t j = 0; j < heights.size(); ++j)
5583  {
5584  uint64_t pos = (heights[j] * resolution) / blockchain_height;
5585  ring_str[pos] = 'o';
5586  }
5587  if (highlight_height < blockchain_height)
5588  {
5589  uint64_t pos = (highlight_height * resolution) / blockchain_height;
5590  ring_str[pos] = '*';
5591  }
5592 
5593  return std::make_pair(ostr.str(), ring_str);
5594 }
5595 //----------------------------------------------------------------------------------------------------
5596 bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
5597 {
5598  uint32_t version;
5599  if (!try_connect_to_daemon(false, &version))
5600  return false;
5601  // available for RPC version 1.4 or higher
5602  if (version < MAKE_CORE_RPC_VERSION(1, 4))
5603  return true;
5604  std::string err;
5605  uint64_t blockchain_height = get_daemon_blockchain_height(err);
5606  if (!err.empty())
5607  {
5608  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
5609  return false;
5610  }
5611  // for each transaction
5612  for (size_t n = 0; n < ptx_vector.size(); ++n)
5613  {
5614  const cryptonote::transaction& tx = ptx_vector[n].tx;
5615  const tools::wallet2::tx_construction_data& construction_data = ptx_vector[n].construction_data;
5616  ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx);
5617  // for each input
5618  std::vector<uint64_t> spent_key_height(tx.vin.size());
5619  std::vector<crypto::hash> spent_key_txid (tx.vin.size());
5620  for (size_t i = 0; i < tx.vin.size(); ++i)
5621  {
5622  if (tx.vin[i].type() != typeid(cryptonote::txin_to_key))
5623  continue;
5624  const cryptonote::txin_to_key& in_key = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
5625  const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(construction_data.selected_transfers[i]);
5626  const cryptonote::tx_source_entry *sptr = NULL;
5627  for (const auto &src: construction_data.sources)
5628  if (src.outputs[src.real_output].second.dest == td.get_public_key())
5629  sptr = &src;
5630  if (!sptr)
5631  {
5632  fail_msg_writer() << tr("failed to find construction data for tx input");
5633  return false;
5634  }
5635  const cryptonote::tx_source_entry& source = *sptr;
5636 
5637  ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_etn(source.amount);
5638  // convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount
5639  std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets);
5640  // get block heights from which those ring member keys originated
5642  req.outputs.resize(absolute_offsets.size());
5643  for (size_t j = 0; j < absolute_offsets.size(); ++j)
5644  {
5645  req.outputs[j].amount = in_key.amount;
5646  req.outputs[j].index = absolute_offsets[j];
5647  }
5649  bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
5650  err = interpret_rpc_response(r, res.status);
5651  if (!err.empty())
5652  {
5653  fail_msg_writer() << tr("failed to get output: ") << err;
5654  return false;
5655  }
5656  // make sure that returned block heights are less than blockchain height
5657  for (auto& res_out : res.outs)
5658  {
5659  if (res_out.height >= blockchain_height)
5660  {
5661  fail_msg_writer() << tr("output key's originating block height shouldn't be higher than the blockchain height");
5662  return false;
5663  }
5664  }
5665  ostr << tr("\nOriginating block heights: ");
5666  spent_key_height[i] = res.outs[source.real_output].height;
5667  spent_key_txid [i] = res.outs[source.real_output].txid;
5668  std::vector<uint64_t> heights(absolute_offsets.size(), 0);
5669  uint64_t highlight_height = std::numeric_limits<uint64_t>::max();
5670  for (size_t j = 0; j < absolute_offsets.size(); ++j)
5671  {
5672  heights[j] = res.outs[j].height;
5673  if (j == source.real_output)
5674  highlight_height = heights[j];
5675  }
5676  std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
5677  ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
5678  }
5679  // warn if rings contain keys originating from the same tx or temporally very close block heights
5680  bool are_keys_from_same_tx = false;
5681  bool are_keys_from_close_height = false;
5682  for (size_t i = 0; i < tx.vin.size(); ++i) {
5683  for (size_t j = i + 1; j < tx.vin.size(); ++j)
5684  {
5685  if (spent_key_txid[i] == spent_key_txid[j])
5686  are_keys_from_same_tx = true;
5687  if (std::abs((int64_t)(spent_key_height[i] - spent_key_height[j])) < (int64_t)5)
5688  are_keys_from_close_height = true;
5689  }
5690  }
5691  if (are_keys_from_same_tx || are_keys_from_close_height)
5692  {
5693  ostr
5694  << tr("\nWarning: Some input keys being spent are from ")
5695  << (are_keys_from_same_tx ? tr("the same transaction") : tr("blocks that are temporally very close"))
5696  << tr(", which can break the anonymity of ring signature. Make sure this is intentional!");
5697  }
5698  ostr << ENDL;
5699  }
5700  return true;
5701 }
5702 //----------------------------------------------------------------------------------------------------
5703 bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms)
5704 {
5705 // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
5706  if (!try_connect_to_daemon())
5707  return false;
5708 
5709  std::vector<std::string> local_args = args_;
5710 
5711  std::set<uint32_t> subaddr_indices;
5712  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
5713  {
5714  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
5715  return false;
5716  local_args.erase(local_args.begin());
5717  }
5718 
5719  uint32_t priority = 0;
5720  if (local_args.size() > 0 && parse_priority(local_args[0], priority))
5721  local_args.erase(local_args.begin());
5722 
5723  priority = m_wallet->adjust_priority(priority);
5724 
5725  size_t fake_outs_count = 0;
5726  if(local_args.size() > 0) {
5727  size_t ring_size;
5728  if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
5729  {
5730  fake_outs_count = m_wallet->default_mixin();
5731  if (fake_outs_count == 0)
5732  fake_outs_count = DEFAULT_MIX;
5733  }
5734  else if (ring_size == 0)
5735  {
5736  fail_msg_writer() << tr("Ring size must not be 0");
5737  return false;
5738  }
5739  else
5740  {
5741  fake_outs_count = ring_size - 1;
5742  local_args.erase(local_args.begin());
5743  }
5744  }
5745 
5746  uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
5747  if (adjusted_fake_outs_count > fake_outs_count)
5748  {
5749  fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
5750  return false;
5751  }
5752  if (adjusted_fake_outs_count < fake_outs_count)
5753  {
5754  fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
5755  return false;
5756  }
5757 
5758  const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
5759  if(local_args.size() < min_args)
5760  {
5761  fail_msg_writer() << tr("wrong number of arguments");
5762  return false;
5763  }
5764 
5765  std::vector<uint8_t> extra;
5766  bool payment_id_seen = false;
5767  if (!local_args.empty())
5768  {
5769  std::string payment_id_str = local_args.back();
5770  crypto::hash payment_id;
5771  bool r = true;
5772  if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id))
5773  {
5775 
5776  std::string extra_nonce;
5777  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
5778  r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
5779  local_args.pop_back();
5780  payment_id_seen = true;
5781  message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead");
5782  }
5783  if(!r)
5784  {
5785  fail_msg_writer() << tr("payment id failed to encode");
5786  return false;
5787  }
5788  }
5789 
5790  uint64_t locked_blocks = 0;
5791  if (transfer_type == TransferLocked)
5792  {
5793  try
5794  {
5795  locked_blocks = boost::lexical_cast<uint64_t>(local_args.back());
5796  }
5797  catch (const std::exception &e)
5798  {
5799  fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back();
5800  return false;
5801  }
5802  if (locked_blocks > 1000000)
5803  {
5804  fail_msg_writer() << tr("Locked blocks too high, max 1000000 (Ëœ4 yrs)");
5805  return false;
5806  }
5807  local_args.pop_back();
5808  }
5809 
5810  vector<cryptonote::address_parse_info> dsts_info;
5811  vector<cryptonote::tx_destination_entry> dsts;
5812  size_t num_subaddresses = 0;
5813  for (size_t i = 0; i < local_args.size(); )
5814  {
5815  dsts_info.emplace_back();
5816  cryptonote::address_parse_info & info = dsts_info.back();
5818  bool r = true;
5819 
5820  // check for a URI
5821  std::string address_uri, payment_id_uri, tx_description, recipient_name, error;
5822  std::vector<std::string> unknown_parameters;
5823  uint64_t amount = 0;
5824  bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters, error);
5825  if (has_uri)
5826  {
5827  r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_uri, oa_prompter);
5828  if (payment_id_uri.size() == 16)
5829  {
5830  if (!tools::wallet2::parse_short_payment_id(payment_id_uri, info.payment_id))
5831  {
5832  fail_msg_writer() << tr("failed to parse short payment ID from URI");
5833  return false;
5834  }
5835  info.has_payment_id = true;
5836  }
5837  de.amount = amount;
5838  de.original = local_args[i];
5839  ++i;
5840  }
5841  else if (i + 1 < local_args.size())
5842  {
5843  r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter);
5844  bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
5845  if(!ok || 0 == de.amount)
5846  {
5847  fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] <<
5848  ", " << tr("expected number from 0 to ") << print_etn(std::numeric_limits<uint64_t>::max());
5849  return false;
5850  }
5851  de.original = local_args[i];
5852  i += 2;
5853  }
5854  else
5855  {
5856  if (boost::starts_with(local_args[i], "electroneum:"))
5857  fail_msg_writer() << tr("Invalid last argument: ") << local_args.back() << ": " << error;
5858  else
5859  fail_msg_writer() << tr("Invalid last argument: ") << local_args.back();
5860  return false;
5861  }
5862 
5863  if (!r)
5864  {
5865  fail_msg_writer() << tr("failed to parse address");
5866  return false;
5867  }
5868  de.addr = info.address;
5869  de.is_subaddress = info.is_subaddress;
5870  de.is_integrated = info.has_payment_id;
5871  num_subaddresses += info.is_subaddress;
5872 
5873  if (info.has_payment_id || !payment_id_uri.empty())
5874  {
5875  if (payment_id_seen)
5876  {
5877  fail_msg_writer() << tr("a single transaction cannot use more than one payment id");
5878  return false;
5879  }
5880 
5881  crypto::hash payment_id;
5882  std::string extra_nonce;
5883  if (info.has_payment_id)
5884  {
5885  memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
5886  memset(payment_id.data + 8, 0, 24); // merely a sanity check
5887  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
5888  }
5889  else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id))
5890  {
5892  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
5893  }
5894  else
5895  {
5896  fail_msg_writer() << tr("failed to parse payment id, though it was detected");
5897  return false;
5898  }
5899  bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
5900  if(!r)
5901  {
5902  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
5903  return false;
5904  }
5905  payment_id_seen = true;
5906  }
5907 
5908  dsts.push_back(de);
5909  }
5910 
5911  // prompt is there is no payment id and confirmation is required
5912  if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
5913  {
5914  std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
5915  if (std::cin.eof())
5916  return false;
5917  if (!command_line::is_yes(accepted))
5918  {
5919  fail_msg_writer() << tr("transaction cancelled.");
5920 
5921  return false;
5922  }
5923  }
5924 
5925  SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
5926 
5927  try
5928  {
5929  // figure out what tx will be necessary
5930  std::vector<tools::wallet2::pending_tx> ptx_vector;
5931  uint64_t bc_height, unlock_block = 0;
5932  std::string err;
5933  switch (transfer_type)
5934  {
5935  case TransferLocked:
5936  bc_height = get_daemon_blockchain_height(err);
5937  if (!err.empty())
5938  {
5939  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
5940  return false;
5941  }
5942  unlock_block = bc_height + locked_blocks;
5943  ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
5944  break;
5945  default:
5946  LOG_ERROR("Unknown transfer method, using default");
5947  /* FALLTHRU */
5948  case Transfer:
5949  ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
5950  break;
5951  }
5952 
5953  if (ptx_vector.empty())
5954  {
5955  fail_msg_writer() << tr("No outputs found, or daemon is not ready");
5956  return false;
5957  }
5958 
5959  // if more than one tx necessary, prompt user to confirm
5960  if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
5961  {
5962  uint64_t total_sent = 0;
5963  uint64_t total_fee = 0;
5964  uint64_t dust_not_in_fee = 0;
5965  uint64_t dust_in_fee = 0;
5966  uint64_t change = 0;
5967  for (size_t n = 0; n < ptx_vector.size(); ++n)
5968  {
5969  total_fee += ptx_vector[n].fee;
5970  for (auto i: ptx_vector[n].selected_transfers)
5971  total_sent += m_wallet->get_transfer_details(i).amount();
5972  total_sent -= ptx_vector[n].change_dts.amount + ptx_vector[n].fee;
5973  change += ptx_vector[n].change_dts.amount;
5974 
5975  if (ptx_vector[n].dust_added_to_fee)
5976  dust_in_fee += ptx_vector[n].dust;
5977  else
5978  dust_not_in_fee += ptx_vector[n].dust;
5979  }
5980 
5981  std::stringstream prompt;
5982  for (size_t n = 0; n < ptx_vector.size(); ++n)
5983  {
5984  prompt << tr("\nTransaction ") << (n + 1) << "/" << ptx_vector.size() << ":\n";
5985  subaddr_indices.clear();
5986  for (uint32_t i : ptx_vector[n].construction_data.subaddr_indices)
5987  subaddr_indices.insert(i);
5988  for (uint32_t i : subaddr_indices)
5989  prompt << boost::format(tr("Spending from address index %d\n")) % i;
5990  if (subaddr_indices.size() > 1)
5991  prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
5992  }
5993  prompt << boost::format(tr("Sending %s. ")) % print_etn(total_sent);
5994  if (ptx_vector.size() > 1)
5995  {
5996  prompt << boost::format(tr("Your transaction needs to be split into %llu transactions. "
5997  "This will result in a transaction fee being applied to each transaction, for a total fee of %s")) %
5998  ((unsigned long long)ptx_vector.size()) % print_etn(total_fee);
5999  }
6000  else
6001  {
6002  prompt << boost::format(tr("The transaction fee is %s")) %
6003  print_etn(total_fee);
6004  }
6005  if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_etn(dust_in_fee);
6006  if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address"))
6007  % print_etn(dust_not_in_fee);
6008  if (transfer_type == TransferLocked)
6009  {
6010  float days = locked_blocks / 720.0f;
6011  prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_etn(change) % ((unsigned long long)unlock_block) % days;
6012  }
6013  if (m_wallet->print_ring_members())
6014  {
6015  if (!print_ring_members(ptx_vector, prompt))
6016  return false;
6017  }
6018  bool default_ring_size = true;
6019  for (const auto &ptx: ptx_vector)
6020  {
6021  for (const auto &vin: ptx.tx.vin)
6022  {
6023  if (vin.type() == typeid(txin_to_key))
6024  {
6025  const txin_to_key& in_to_key = boost::get<txin_to_key>(vin);
6026  if (in_to_key.key_offsets.size() != DEFAULT_MIX + 1)
6027  default_ring_size = false;
6028  }
6029  }
6030  }
6031  if (m_wallet->confirm_non_default_ring_size() && !default_ring_size)
6032  {
6033  prompt << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
6034  }
6035  prompt << ENDL << tr("Is this okay?");
6036 
6037  std::string accepted = input_line(prompt.str(), true);
6038  if (std::cin.eof())
6039  return false;
6040  if (!command_line::is_yes(accepted))
6041  {
6042  fail_msg_writer() << tr("transaction cancelled.");
6043 
6044  return false;
6045  }
6046 
6047  }
6048 
6049  // actually commit the transactions
6050  if (m_wallet->multisig() && called_by_mms)
6051  {
6052  std::string ciphertext = m_wallet->save_multisig_tx(ptx_vector);
6053  if (!ciphertext.empty())
6054  {
6055  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::partially_signed_tx, ciphertext);
6056  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to MMS");
6057  }
6058  }
6059  else if (m_wallet->multisig())
6060  {
6061  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6062  if (!r)
6063  {
6064  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6065  return false;
6066  }
6067  else
6068  {
6069  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6070  }
6071  }
6072  else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6073  {
6074  try
6075  {
6077  if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
6078  fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
6079  return false;
6080  }
6081 
6082  commit_or_save(signed_tx.ptx, m_do_not_relay);
6083  }
6084  catch (const std::exception& e)
6085  {
6086  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6087  return false;
6088  }
6089  catch (...)
6090  {
6091  LOG_ERROR("Unknown error");
6092  fail_msg_writer() << tr("unknown error");
6093  return false;
6094  }
6095  }
6096  else if (m_wallet->watch_only())
6097  {
6098  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6099  if (!r)
6100  {
6101  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6102  return false;
6103  }
6104  else
6105  {
6106  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6107  }
6108  }
6109  else
6110  {
6111  commit_or_save(ptx_vector, m_do_not_relay);
6112  }
6113  }
6114  catch (const std::exception &e)
6115  {
6116  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6117  return false;
6118  }
6119  catch (...)
6120  {
6121  LOG_ERROR("unknown error");
6122  fail_msg_writer() << tr("unknown error");
6123  return false;
6124  }
6125 
6126  return true;
6127 }
6128 //----------------------------------------------------------------------------------------------------
6129 bool simple_wallet::transfer(const std::vector<std::string> &args_)
6130 {
6131  transfer_main(Transfer, args_, false);
6132  return true;
6133 }
6134 //----------------------------------------------------------------------------------------------------
6135 bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
6136 {
6137  transfer_main(TransferLocked, args_, false);
6138  return true;
6139 }
6140 //----------------------------------------------------------------------------------------------------
6141 bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
6142 {
6143  return sweep_main(0, true, args_);
6144 }
6145 //----------------------------------------------------------------------------------------------------
6146 
6147 bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
6148 {
6149  if (!try_connect_to_daemon())
6150  return true;
6151 
6153 
6154  try
6155  {
6156  // figure out what tx will be necessary
6157  auto ptx_vector = m_wallet->create_unmixable_sweep_transactions();
6158 
6159  if (ptx_vector.empty())
6160  {
6161  fail_msg_writer() << tr("No unmixable outputs found");
6162  return true;
6163  }
6164 
6165  // give user total and fee, and prompt to confirm
6166  uint64_t total_fee = 0, total_unmixable = 0;
6167  for (size_t n = 0; n < ptx_vector.size(); ++n)
6168  {
6169  total_fee += ptx_vector[n].fee;
6170  for (auto i: ptx_vector[n].selected_transfers)
6171  total_unmixable += m_wallet->get_transfer_details(i).amount();
6172  }
6173 
6174  std::string prompt_str = tr("Sweeping ") + print_etn(total_unmixable);
6175  if (ptx_vector.size() > 1) {
6176  prompt_str = (boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6177  print_etn(total_unmixable) %
6178  ((unsigned long long)ptx_vector.size()) %
6179  print_etn(total_fee)).str();
6180  }
6181  else {
6182  prompt_str = (boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
6183  print_etn(total_unmixable) %
6184  print_etn(total_fee)).str();
6185  }
6186  std::string accepted = input_line(prompt_str, true);
6187  if (std::cin.eof())
6188  return true;
6189  if (!command_line::is_yes(accepted))
6190  {
6191  fail_msg_writer() << tr("transaction cancelled.");
6192 
6193  return true;
6194  }
6195 
6196  // actually commit the transactions
6197  if (m_wallet->multisig())
6198  {
6199  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6200  if (!r)
6201  {
6202  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6203  }
6204  else
6205  {
6206  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6207  }
6208  }
6209  else if (m_wallet->watch_only())
6210  {
6211  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6212  if (!r)
6213  {
6214  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6215  }
6216  else
6217  {
6218  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6219  }
6220  }
6221  else
6222  {
6223  commit_or_save(ptx_vector, m_do_not_relay);
6224  }
6225  }
6226  catch (const tools::error::not_enough_unlocked_etn& e)
6227  {
6228  fail_msg_writer() << tr("Not enough ETN in unlocked balance");
6229  std::string accepted = input_line((boost::format(tr("Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay?")) % print_etn(e.available())).str(), true);
6230  if (std::cin.eof())
6231  return true;
6232  if (command_line::is_yes(accepted))
6233  {
6234  try
6235  {
6236  m_wallet->discard_unmixable_outputs();
6237  } catch (...) {}
6238  }
6239  }
6240  catch (const std::exception &e)
6241  {
6242  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6243  }
6244  catch (...)
6245  {
6246  LOG_ERROR("unknown error");
6247  fail_msg_writer() << tr("unknown error");
6248  }
6249 
6250  return true;
6251 }
6252 //----------------------------------------------------------------------------------------------------
6253 bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<std::string> &args_)
6254 {
6255  auto print_usage = [below]()
6256  {
6257  if (below)
6258  {
6259  PRINT_USAGE(USAGE_SWEEP_BELOW);
6260  }
6261  else
6262  {
6263  PRINT_USAGE(USAGE_SWEEP_ALL);
6264  }
6265  };
6266  if (args_.size() == 0)
6267  {
6268  fail_msg_writer() << tr("No address given");
6269  print_usage();
6270  return true;
6271  }
6272 
6273  if (!try_connect_to_daemon())
6274  return true;
6275 
6276  std::vector<std::string> local_args = args_;
6277 
6278  std::set<uint32_t> subaddr_indices;
6279  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
6280  {
6281  if (local_args[0] == "index=all")
6282  {
6283  for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i)
6284  subaddr_indices.insert(i);
6285  }
6286  else if (!parse_subaddress_indices(local_args[0], subaddr_indices))
6287  {
6288  print_usage();
6289  return true;
6290  }
6291  local_args.erase(local_args.begin());
6292  }
6293 
6294  uint32_t priority = 0;
6295  if (local_args.size() > 0 && parse_priority(local_args[0], priority))
6296  local_args.erase(local_args.begin());
6297 
6298  priority = m_wallet->adjust_priority(priority);
6299 
6300  size_t fake_outs_count = 0;
6301  if(local_args.size() > 0) {
6302  size_t ring_size;
6303  if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
6304  {
6305  fake_outs_count = m_wallet->default_mixin();
6306  if (fake_outs_count == 0)
6307  fake_outs_count = DEFAULT_MIX;
6308  }
6309  else if (ring_size == 0)
6310  {
6311  fail_msg_writer() << tr("Ring size must not be 0");
6312  return true;
6313  }
6314  else
6315  {
6316  fake_outs_count = ring_size - 1;
6317  local_args.erase(local_args.begin());
6318  }
6319  }
6320 
6321  uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6322  if (adjusted_fake_outs_count > fake_outs_count)
6323  {
6324  fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6325  return true;
6326  }
6327  if (adjusted_fake_outs_count < fake_outs_count)
6328  {
6329  fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6330  return true;
6331  }
6332 
6333  uint64_t unlock_block = 0;
6334  if (locked) {
6335  uint64_t locked_blocks = 0;
6336 
6337  if (local_args.size() < 2) {
6338  fail_msg_writer() << tr("missing lockedblocks parameter");
6339  return true;
6340  }
6341 
6342  try
6343  {
6344  locked_blocks = boost::lexical_cast<uint64_t>(local_args[1]);
6345  }
6346  catch (const std::exception &e)
6347  {
6348  fail_msg_writer() << tr("bad locked_blocks parameter");
6349  return true;
6350  }
6351  if (locked_blocks > 1000000)
6352  {
6353  fail_msg_writer() << tr("Locked blocks too high, max 1000000 (Ëœ4 yrs)");
6354  return true;
6355  }
6356  std::string err;
6357  uint64_t bc_height = get_daemon_blockchain_height(err);
6358  if (!err.empty())
6359  {
6360  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
6361  return true;
6362  }
6363  unlock_block = bc_height + locked_blocks;
6364 
6365  local_args.erase(local_args.begin() + 1);
6366  }
6367 
6368  size_t outputs = 1;
6369  if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=")
6370  {
6371  if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8)))
6372  {
6373  fail_msg_writer() << tr("Failed to parse number of outputs");
6374  return true;
6375  }
6376  else if (outputs < 1)
6377  {
6378  fail_msg_writer() << tr("Amount of outputs should be greater than 0");
6379  return true;
6380  }
6381  else
6382  {
6383  local_args.erase(local_args.begin());
6384  }
6385  }
6386 
6387  std::vector<uint8_t> extra;
6388  bool payment_id_seen = false;
6389  if (local_args.size() >= 2)
6390  {
6391  std::string payment_id_str = local_args.back();
6392 
6393  crypto::hash payment_id;
6394  bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
6395  if(r)
6396  {
6398 
6399  std::string extra_nonce;
6400  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6401  r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
6402  payment_id_seen = true;
6403  }
6404 
6405  if(!r && local_args.size() == 3)
6406  {
6407  fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str;
6408  print_usage();
6409  return true;
6410  }
6411  if (payment_id_seen)
6412  local_args.pop_back();
6413  }
6414 
6416  if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[0], oa_prompter))
6417  {
6418  fail_msg_writer() << tr("failed to parse address");
6419  print_usage();
6420  return true;
6421  }
6422 
6423  if (info.has_payment_id)
6424  {
6425  if (payment_id_seen)
6426  {
6427  fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[0];
6428  return true;
6429  }
6430 
6431  std::string extra_nonce;
6432  crypto::hash payment_id = crypto::null_hash;
6433  memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
6434  memset(payment_id.data + 8, 0, 24); // merely a sanity check
6435 
6436  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6437  bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
6438  if(!r)
6439  {
6440  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
6441  return true;
6442  }
6443  payment_id_seen = true;
6444  }
6445 
6446  // prompt is there is no payment id and confirmation is required
6447  if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
6448  {
6449  std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
6450  if (std::cin.eof())
6451  return true;
6452  if (!command_line::is_yes(accepted))
6453  {
6454  fail_msg_writer() << tr("transaction cancelled.");
6455 
6456  return true;
6457  }
6458  }
6459 
6461 
6462  try
6463  {
6464  // figure out what tx will be necessary
6465  auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
6466 
6467  if (ptx_vector.empty())
6468  {
6469  fail_msg_writer() << tr("No outputs found, or daemon is not ready");
6470  return true;
6471  }
6472 
6473  // give user total and fee, and prompt to confirm
6474  uint64_t total_fee = 0, total_sent = 0;
6475  for (size_t n = 0; n < ptx_vector.size(); ++n)
6476  {
6477  total_fee += ptx_vector[n].fee;
6478  for (auto i: ptx_vector[n].selected_transfers)
6479  total_sent += m_wallet->get_transfer_details(i).amount();
6480  }
6481 
6482  std::ostringstream prompt;
6483  for (size_t n = 0; n < ptx_vector.size(); ++n)
6484  {
6485  prompt << tr("\nTransaction ") << (n + 1) << "/" << ptx_vector.size() << ":\n";
6486  subaddr_indices.clear();
6487  for (uint32_t i : ptx_vector[n].construction_data.subaddr_indices)
6488  subaddr_indices.insert(i);
6489  for (uint32_t i : subaddr_indices)
6490  prompt << boost::format(tr("Spending from address index %d\n")) % i;
6491  if (subaddr_indices.size() > 1)
6492  prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
6493  }
6494  if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
6495  return true;
6496  if (ptx_vector.size() > 1) {
6497  prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6498  print_etn(total_sent) %
6499  ((unsigned long long)ptx_vector.size()) %
6500  print_etn(total_fee);
6501  }
6502  else {
6503  prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
6504  print_etn(total_sent) %
6505  print_etn(total_fee);
6506  }
6507  std::string accepted = input_line(prompt.str(), true);
6508  if (std::cin.eof())
6509  return true;
6510  if (!command_line::is_yes(accepted))
6511  {
6512  fail_msg_writer() << tr("transaction cancelled.");
6513 
6514  return true;
6515  }
6516 
6517  // actually commit the transactions
6518  if (m_wallet->multisig())
6519  {
6520  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6521  if (!r)
6522  {
6523  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6524  }
6525  else
6526  {
6527  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6528  }
6529  }
6530  else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6531  {
6532  try
6533  {
6535  std::vector<cryptonote::address_parse_info> dsts_info;
6536  dsts_info.push_back(info);
6537 
6538  if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
6539  fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
6540  return true;
6541  }
6542 
6543  commit_or_save(signed_tx.ptx, m_do_not_relay);
6544  }
6545  catch (const std::exception& e)
6546  {
6547  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6548  }
6549  catch (...)
6550  {
6551  LOG_ERROR("Unknown error");
6552  fail_msg_writer() << tr("unknown error");
6553  }
6554  }
6555  else if (m_wallet->watch_only())
6556  {
6557  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6558  if (!r)
6559  {
6560  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6561  }
6562  else
6563  {
6564  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6565  }
6566  }
6567  else
6568  {
6569  commit_or_save(ptx_vector, m_do_not_relay);
6570  }
6571  }
6572  catch (const std::exception& e)
6573  {
6574  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6575  }
6576  catch (...)
6577  {
6578  LOG_ERROR("unknown error");
6579  fail_msg_writer() << tr("unknown error");
6580  }
6581 
6582  return true;
6583 }
6584 //----------------------------------------------------------------------------------------------------
6585 bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
6586 {
6587  if (!try_connect_to_daemon())
6588  return true;
6589 
6590  std::vector<std::string> local_args = args_;
6591 
6592  uint32_t priority = 0;
6593  if (local_args.size() > 0 && parse_priority(local_args[0], priority))
6594  local_args.erase(local_args.begin());
6595 
6596  priority = m_wallet->adjust_priority(priority);
6597 
6598  size_t fake_outs_count = 0;
6599  if(local_args.size() > 0) {
6600  size_t ring_size;
6601  if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
6602  {
6603  fake_outs_count = m_wallet->default_mixin();
6604  if (fake_outs_count == 0)
6605  fake_outs_count = DEFAULT_MIX;
6606  }
6607  else if (ring_size == 0)
6608  {
6609  fail_msg_writer() << tr("Ring size must not be 0");
6610  return true;
6611  }
6612  else
6613  {
6614  fake_outs_count = ring_size - 1;
6615  local_args.erase(local_args.begin());
6616  }
6617  }
6618 
6619  uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6620  if (adjusted_fake_outs_count > fake_outs_count)
6621  {
6622  fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6623  return true;
6624  }
6625  if (adjusted_fake_outs_count < fake_outs_count)
6626  {
6627  fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6628  return true;
6629  }
6630 
6631  size_t outputs = 1;
6632  if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=")
6633  {
6634  if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8)))
6635  {
6636  fail_msg_writer() << tr("Failed to parse number of outputs");
6637  return true;
6638  }
6639  else if (outputs < 1)
6640  {
6641  fail_msg_writer() << tr("Amount of outputs should be greater than 0");
6642  return true;
6643  }
6644  else
6645  {
6646  local_args.erase(local_args.begin());
6647  }
6648  }
6649 
6650  std::vector<uint8_t> extra;
6651  bool payment_id_seen = false;
6652  if (local_args.size() == 3)
6653  {
6654  crypto::hash payment_id;
6655  crypto::hash8 payment_id8;
6656  std::string extra_nonce;
6657  if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id))
6658  {
6660  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6661  }
6662  else
6663  {
6664  fail_msg_writer() << tr("failed to parse Payment ID");
6665  return true;
6666  }
6667 
6668  if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
6669  {
6670  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
6671  return true;
6672  }
6673 
6674  local_args.pop_back();
6675  payment_id_seen = true;
6676  }
6677 
6678  if (local_args.size() != 2)
6679  {
6680  PRINT_USAGE(USAGE_SWEEP_SINGLE);
6681  return true;
6682  }
6683 
6684  crypto::key_image ki;
6685  if (!epee::string_tools::hex_to_pod(local_args[0], ki))
6686  {
6687  fail_msg_writer() << tr("failed to parse key image");
6688  return true;
6689  }
6690 
6692  if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[1], oa_prompter))
6693  {
6694  fail_msg_writer() << tr("failed to parse address");
6695  return true;
6696  }
6697 
6698  if (info.has_payment_id)
6699  {
6700  if (payment_id_seen)
6701  {
6702  fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[0];
6703  return true;
6704  }
6705 
6706  std::string extra_nonce;
6707  crypto::hash payment_id = crypto::null_hash;
6708  memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
6709  memset(payment_id.data + 8, 0, 24); // merely a sanity check
6710 
6711  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6712  if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
6713  {
6714  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
6715  return true;
6716  }
6717  payment_id_seen = true;
6718  }
6719 
6720  // prompt if there is no payment id and confirmation is required
6721  if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
6722  {
6723  std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
6724  if (std::cin.eof())
6725  return true;
6726  if (!command_line::is_yes(accepted))
6727  {
6728  fail_msg_writer() << tr("transaction cancelled.");
6729 
6730  // would like to return false, because no tx made, but everything else returns true
6731  // and I don't know what returning false might adversely affect. *sigh*
6732  return true;
6733  }
6734  }
6735 
6737 
6738  try
6739  {
6740  // figure out what tx will be necessary
6741  auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra);
6742 
6743  if (ptx_vector.empty())
6744  {
6745  fail_msg_writer() << tr("No outputs found");
6746  return true;
6747  }
6748  if (ptx_vector.size() > 1)
6749  {
6750  fail_msg_writer() << tr("Multiple transactions are created, which is not supposed to happen");
6751  return true;
6752  }
6753  if (ptx_vector[0].selected_transfers.size() != 1)
6754  {
6755  fail_msg_writer() << tr("The transaction uses multiple or no inputs, which is not supposed to happen");
6756  return true;
6757  }
6758 
6759  // give user total and fee, and prompt to confirm
6760  uint64_t total_fee = ptx_vector[0].fee;
6761  uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount();
6762  std::ostringstream prompt;
6763  if (!print_ring_members(ptx_vector, prompt))
6764  return true;
6765  prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
6766  print_etn(total_sent) %
6767  print_etn(total_fee);
6768  std::string accepted = input_line(prompt.str(), true);
6769  if (std::cin.eof())
6770  return true;
6771  if (!command_line::is_yes(accepted))
6772  {
6773  fail_msg_writer() << tr("transaction cancelled.");
6774  return true;
6775  }
6776 
6777  // actually commit the transactions
6778  if (m_wallet->multisig())
6779  {
6780  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6781  if (!r)
6782  {
6783  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6784  }
6785  else
6786  {
6787  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6788  }
6789  }
6790  else if (m_wallet->watch_only())
6791  {
6792  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6793  if (!r)
6794  {
6795  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6796  }
6797  else
6798  {
6799  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6800  }
6801  }
6802  else
6803  {
6804  m_wallet->commit_tx(ptx_vector[0]);
6805  success_msg_writer(true) << tr("ETN successfully sent, transaction: ") << get_transaction_hash(ptx_vector[0].tx);
6806  }
6807 
6808  }
6809  catch (const std::exception& e)
6810  {
6811  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6812  }
6813  catch (...)
6814  {
6815  LOG_ERROR("unknown error");
6816  fail_msg_writer() << tr("unknown error");
6817  }
6818 
6819  return true;
6820 }
6821 //----------------------------------------------------------------------------------------------------
6822 bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
6823 {
6824  return sweep_main(0, false, args_);
6825 }
6826 //----------------------------------------------------------------------------------------------------
6827 bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
6828 {
6829  uint64_t below = 0;
6830  if (args_.size() < 1)
6831  {
6832  fail_msg_writer() << tr("missing threshold amount");
6833  return true;
6834  }
6835  if (!cryptonote::parse_amount(below, args_[0]))
6836  {
6837  fail_msg_writer() << tr("invalid amount threshold");
6838  return true;
6839  }
6840  return sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end()));
6841 }
6842 //----------------------------------------------------------------------------------------------------
6843 bool simple_wallet::donate(const std::vector<std::string> &args_)
6844 {
6845  std::vector<std::string> local_args = args_;
6846  if(local_args.empty() || local_args.size() > 5)
6847  {
6848  PRINT_USAGE(USAGE_DONATE);
6849  return true;
6850  }
6851  std::string amount_str;
6852  std::string payment_id_str;
6853  // get payment id and pop
6854  crypto::hash payment_id;
6855  crypto::hash8 payment_id8;
6856  if (tools::wallet2::parse_long_payment_id (local_args.back(), payment_id ) ||
6857  tools::wallet2::parse_short_payment_id(local_args.back(), payment_id8))
6858  {
6859  payment_id_str = local_args.back();
6860  local_args.pop_back();
6861  }
6862  // get amount and pop
6863  uint64_t amount;
6864  bool ok = cryptonote::parse_amount(amount, local_args.back());
6865  if (ok && amount != 0)
6866  {
6867  amount_str = local_args.back();
6868  local_args.pop_back();
6869  }
6870  else
6871  {
6872  fail_msg_writer() << tr("amount is wrong: ") << local_args.back() << ", " << tr("expected number from 0 to ") << print_etn(std::numeric_limits<uint64_t>::max());
6873  return true;
6874  }
6875  // push back address, amount, payment id
6876  std::string address_str;
6877  if (m_wallet->nettype() != cryptonote::MAINNET)
6878  {
6879  // if not mainnet, convert donation address string to the relevant network type
6882  {
6883  fail_msg_writer() << tr("Failed to parse donation address: ") << ETN_DONATION_ADDR;
6884  return true;
6885  }
6886  address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
6887  }
6888  else
6889  {
6890  address_str = ETN_DONATION_ADDR;
6891  }
6892  local_args.push_back(address_str);
6893  local_args.push_back(amount_str);
6894  if (!payment_id_str.empty())
6895  local_args.push_back(payment_id_str);
6896  if (m_wallet->nettype() == cryptonote::MAINNET)
6897  message_writer() << (boost::format(tr("Donating %s %s to The Electroneum Project (%s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % ETN_DONATION_ADDR).str();
6898  else
6899  message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str();
6900  transfer(local_args);
6901  return true;
6902 }
6903 //----------------------------------------------------------------------------------------------------
6904 bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
6905 {
6906  // gather info to ask the user
6907  uint64_t amount = 0, amount_to_dests = 0, change = 0;
6908  size_t min_ring_size = ~0;
6909  std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
6910  int first_known_non_zero_change_index = -1;
6911  std::string payment_id_string = "";
6912  for (size_t n = 0; n < get_num_txes(); ++n)
6913  {
6914  const tools::wallet2::tx_construction_data &cd = get_tx(n);
6915 
6916  std::vector<tx_extra_field> tx_extra_fields;
6917  bool has_encrypted_payment_id = false;
6918  crypto::hash8 payment_id8 = crypto::null_hash8;
6919  if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
6920  {
6921  tx_extra_nonce extra_nonce;
6922  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
6923  {
6924  crypto::hash payment_id;
6925  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
6926  {
6927  if (!payment_id_string.empty())
6928  payment_id_string += ", ";
6929 
6930  // if none of the addresses are integrated addresses, it's a dummy one
6931  bool is_dummy = true;
6932  for (const auto &e: cd.dests)
6933  if (e.is_integrated)
6934  is_dummy = false;
6935 
6936  if (is_dummy)
6937  {
6938  payment_id_string += std::string("dummy payment ID");
6939  }
6940  else
6941  {
6942  crypto::hash payment_id = crypto::null_hash;
6943  memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
6944  memset(payment_id.data + 8, 0, 24); // merely a sanity check
6945  payment_id_string += std::string("payment ID ") + epee::string_tools::pod_to_hex(payment_id);
6946  }
6947  }
6948  else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
6949  {
6950  if (!payment_id_string.empty())
6951  payment_id_string += ", ";
6952  payment_id_string += std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id);
6953  payment_id_string += " (OBSOLETE)";
6954  }
6955  }
6956  }
6957 
6958  for (size_t s = 0; s < cd.sources.size(); ++s)
6959  {
6960  amount += cd.sources[s].amount;
6961  size_t ring_size = cd.sources[s].outputs.size();
6962  if (ring_size < min_ring_size)
6963  min_ring_size = ring_size;
6964  }
6965  for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
6966  {
6967  const tx_destination_entry &entry = cd.splitted_dsts[d];
6968  std::string address, standard_address = get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
6969  if (has_encrypted_payment_id && !entry.is_subaddress && standard_address != entry.original)
6970  {
6971  address = get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
6972  address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")");
6973  }
6974  else
6975  address = standard_address;
6976  auto i = dests.find(entry.addr);
6977  if (i == dests.end())
6978  dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
6979  else
6980  i->second.second += entry.amount;
6981  amount_to_dests += entry.amount;
6982  }
6983  if (cd.change_dts.amount > 0)
6984  {
6985  auto it = dests.find(cd.change_dts.addr);
6986  if (it == dests.end())
6987  {
6988  fail_msg_writer() << tr("Claimed change does not go to a paid address");
6989  return false;
6990  }
6991  if (it->second.second < cd.change_dts.amount)
6992  {
6993  fail_msg_writer() << tr("Claimed change is larger than payment to the change address");
6994  return false;
6995  }
6996  if (cd.change_dts.amount > 0)
6997  {
6998  if (first_known_non_zero_change_index == -1)
6999  first_known_non_zero_change_index = n;
7000  if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr)))
7001  {
7002  fail_msg_writer() << tr("Change goes to more than one address");
7003  return false;
7004  }
7005  }
7006  change += cd.change_dts.amount;
7007  it->second.second -= cd.change_dts.amount;
7008  if (it->second.second == 0)
7009  dests.erase(cd.change_dts.addr);
7010  }
7011  }
7012 
7013  if (payment_id_string.empty())
7014  payment_id_string = "no payment ID";
7015 
7016  std::string dest_string;
7017  size_t n_dummy_outputs = 0;
7018  for (auto i = dests.begin(); i != dests.end(); )
7019  {
7020  if (i->second.second > 0)
7021  {
7022  if (!dest_string.empty())
7023  dest_string += ", ";
7024  dest_string += (boost::format(tr("sending %s to %s")) % print_etn(i->second.second) % i->second.first).str();
7025  }
7026  else
7027  ++n_dummy_outputs;
7028  ++i;
7029  }
7030  if (n_dummy_outputs > 0)
7031  {
7032  if (!dest_string.empty())
7033  dest_string += ", ";
7034  dest_string += std::to_string(n_dummy_outputs) + tr(" dummy output(s)");
7035  }
7036  if (dest_string.empty())
7037  dest_string = tr("with no destinations");
7038 
7039  std::string change_string;
7040  if (change > 0)
7041  {
7042  std::string address = get_account_address_as_str(m_wallet->nettype(), get_tx(0).subaddr_account > 0, get_tx(0).change_dts.addr);
7043  change_string += (boost::format(tr("%s change to %s")) % print_etn(change) % address).str();
7044  }
7045  else
7046  change_string += tr("no change");
7047 
7048  uint64_t fee = amount - amount_to_dests;
7049  std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay?")) % (unsigned long)get_num_txes() % print_etn(amount) % print_etn(fee) % dest_string % change_string % (unsigned long)min_ring_size % payment_id_string % extra_message).str();
7050  return command_line::is_yes(input_line(prompt_str, true));
7051 }
7052 //----------------------------------------------------------------------------------------------------
7053 bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
7054 {
7055  std::string extra_message;
7056  if (!txs.transfers.second.empty())
7057  extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str();
7058  return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message);
7059 }
7060 //----------------------------------------------------------------------------------------------------
7061 bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs)
7062 {
7063  std::string extra_message;
7064  if (!txs.key_images.empty())
7065  extra_message = (boost::format("%u key images to import. ") % (unsigned)txs.key_images.size()).str();
7066  return accept_loaded_tx([&txs](){return txs.ptx.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.ptx[n].construction_data;}, extra_message);
7067 }
7068 //----------------------------------------------------------------------------------------------------
7069 bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
7070 {
7071  if (m_wallet->key_on_device())
7072  {
7073  fail_msg_writer() << tr("command not supported by HW wallet");
7074  return true;
7075  }
7076  if(m_wallet->multisig())
7077  {
7078  fail_msg_writer() << tr("This is a multisig wallet, it can only sign with sign_multisig");
7079  return true;
7080  }
7081  if(m_wallet->watch_only())
7082  {
7083  fail_msg_writer() << tr("This is a watch only wallet");
7084  return true;
7085  }
7086  if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export_raw"))
7087  {
7088  PRINT_USAGE(USAGE_SIGN_TRANSFER);
7089  return true;
7090  }
7091 
7093  const bool export_raw = args_.size() == 1;
7094 
7095  std::vector<tools::wallet2::pending_tx> ptx;
7096  try
7097  {
7098  bool r = m_wallet->sign_tx("unsigned_electroneum_tx", "signed_electroneum_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }, export_raw);
7099  if (!r)
7100  {
7101  fail_msg_writer() << tr("Failed to sign transaction");
7102  return true;
7103  }
7104  }
7105  catch (const std::exception &e)
7106  {
7107  fail_msg_writer() << tr("Failed to sign transaction: ") << e.what();
7108  return true;
7109  }
7110 
7111  std::string txids_as_text;
7112  for (const auto &t: ptx)
7113  {
7114  if (!txids_as_text.empty())
7115  txids_as_text += (", ");
7116  txids_as_text += epee::string_tools::pod_to_hex(get_transaction_hash(t.tx));
7117  }
7118  success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_electroneum_tx" << ", txid " << txids_as_text;
7119  if (export_raw)
7120  {
7121  std::string rawfiles_as_text;
7122  for (size_t i = 0; i < ptx.size(); ++i)
7123  {
7124  if (i > 0)
7125  rawfiles_as_text += ", ";
7126  rawfiles_as_text += "signed_electroneum_tx_raw" + (ptx.size() == 1 ? "" : ("_" + std::to_string(i)));
7127  }
7128  success_msg_writer(true) << tr("Transaction raw hex data exported to ") << rawfiles_as_text;
7129  }
7130  return true;
7131 }
7132 //----------------------------------------------------------------------------------------------------
7133 bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
7134 {
7135  if (m_wallet->key_on_device())
7136  {
7137  fail_msg_writer() << tr("command not supported by HW wallet");
7138  return true;
7139  }
7140  if (!try_connect_to_daemon())
7141  return true;
7142 
7143  try
7144  {
7145  std::vector<tools::wallet2::pending_tx> ptx_vector;
7146  bool r = m_wallet->load_tx("signed_electroneum_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); });
7147  if (!r)
7148  {
7149  fail_msg_writer() << tr("Failed to load transaction from file");
7150  return true;
7151  }
7152 
7153  commit_or_save(ptx_vector, false);
7154  }
7155  catch (const std::exception& e)
7156  {
7157  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
7158  }
7159  catch (...)
7160  {
7161  LOG_ERROR("Unknown error");
7162  fail_msg_writer() << tr("unknown error");
7163  }
7164 
7165  return true;
7166 }
7167 //----------------------------------------------------------------------------------------------------
7168 bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
7169 {
7170  std::vector<std::string> local_args = args_;
7171 
7172  if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR)
7173  {
7174  fail_msg_writer() << tr("command not supported by HW wallet");
7175  return true;
7176  }
7177  if(local_args.size() != 1) {
7178  PRINT_USAGE(USAGE_GET_TX_KEY);
7179  return true;
7180  }
7181 
7182  crypto::hash txid;
7183  if (!epee::string_tools::hex_to_pod(local_args[0], txid))
7184  {
7185  fail_msg_writer() << tr("failed to parse txid");
7186  return true;
7187  }
7188 
7190 
7191  crypto::secret_key tx_key;
7192  std::vector<crypto::secret_key> additional_tx_keys;
7193 
7194  bool found_tx_key = m_wallet->get_tx_key(txid, tx_key, additional_tx_keys);
7195  if (found_tx_key)
7196  {
7197  ostringstream oss;
7198  oss << epee::string_tools::pod_to_hex(tx_key);
7199  for (size_t i = 0; i < additional_tx_keys.size(); ++i)
7200  oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
7201  success_msg_writer() << tr("Tx key: ") << oss.str();
7202  return true;
7203  }
7204  else
7205  {
7206  fail_msg_writer() << tr("no tx keys found for this txid");
7207  return true;
7208  }
7209 }
7210 //----------------------------------------------------------------------------------------------------
7211 bool simple_wallet::set_tx_key(const std::vector<std::string> &args_)
7212 {
7213  std::vector<std::string> local_args = args_;
7214 
7215  if(local_args.size() != 2) {
7216  PRINT_USAGE(USAGE_SET_TX_KEY);
7217  return true;
7218  }
7219 
7220  crypto::hash txid;
7221  if (!epee::string_tools::hex_to_pod(local_args[0], txid))
7222  {
7223  fail_msg_writer() << tr("failed to parse txid");
7224  return true;
7225  }
7226 
7227  crypto::secret_key tx_key;
7228  std::vector<crypto::secret_key> additional_tx_keys;
7229  try
7230  {
7231  if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key))
7232  {
7233  fail_msg_writer() << tr("failed to parse tx_key");
7234  return true;
7235  }
7236  while(true)
7237  {
7238  local_args[1] = local_args[1].substr(64);
7239  if (local_args[1].empty())
7240  break;
7241  additional_tx_keys.resize(additional_tx_keys.size() + 1);
7242  if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back()))
7243  {
7244  fail_msg_writer() << tr("failed to parse tx_key");
7245  return true;
7246  }
7247  }
7248  }
7249  catch (const std::out_of_range &e)
7250  {
7251  fail_msg_writer() << tr("failed to parse tx_key");
7252  return true;
7253  }
7254 
7255  LOCK_IDLE_SCOPE();
7256 
7257  try
7258  {
7259  m_wallet->set_tx_key(txid, tx_key, additional_tx_keys);
7260  success_msg_writer() << tr("Tx key successfully stored.");
7261  }
7262  catch (const std::exception &e)
7263  {
7264  fail_msg_writer() << tr("Failed to store tx key: ") << e.what();
7265  }
7266  return true;
7267 }
7268 //----------------------------------------------------------------------------------------------------
7269 bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
7270 {
7271  if (args.size() != 2 && args.size() != 3)
7272  {
7273  PRINT_USAGE(USAGE_GET_TX_PROOF);
7274  return true;
7275  }
7276 
7277  crypto::hash txid;
7278  if(!epee::string_tools::hex_to_pod(args[0], txid))
7279  {
7280  fail_msg_writer() << tr("failed to parse txid");
7281  return true;
7282  }
7283 
7285  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[1], oa_prompter))
7286  {
7287  fail_msg_writer() << tr("failed to parse address");
7288  return true;
7289  }
7290 
7292 
7293  try
7294  {
7295  std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : "");
7296  const std::string filename = "electroneum_tx_proof";
7297  if (epee::file_io_utils::save_string_to_file(filename, sig_str))
7298  success_msg_writer() << tr("signature file saved to: ") << filename;
7299  else
7300  fail_msg_writer() << tr("failed to save signature file");
7301  }
7302  catch (const std::exception &e)
7303  {
7304  fail_msg_writer() << tr("error: ") << e.what();
7305  }
7306  return true;
7307 }
7308 //----------------------------------------------------------------------------------------------------
7309 bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
7310 {
7311  std::vector<std::string> local_args = args_;
7312 
7313  if(local_args.size() != 3) {
7314  PRINT_USAGE(USAGE_CHECK_TX_KEY);
7315  return true;
7316  }
7317 
7318  if (!try_connect_to_daemon())
7319  return true;
7320 
7321  if (!m_wallet)
7322  {
7323  fail_msg_writer() << tr("wallet is null");
7324  return true;
7325  }
7326  crypto::hash txid;
7327  if(!epee::string_tools::hex_to_pod(local_args[0], txid))
7328  {
7329  fail_msg_writer() << tr("failed to parse txid");
7330  return true;
7331  }
7332 
7333  crypto::secret_key tx_key;
7334  std::vector<crypto::secret_key> additional_tx_keys;
7335  if(!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key))
7336  {
7337  fail_msg_writer() << tr("failed to parse tx key");
7338  return true;
7339  }
7340  local_args[1] = local_args[1].substr(64);
7341  while (!local_args[1].empty())
7342  {
7343  additional_tx_keys.resize(additional_tx_keys.size() + 1);
7344  if(!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back()))
7345  {
7346  fail_msg_writer() << tr("failed to parse tx key");
7347  return true;
7348  }
7349  local_args[1] = local_args[1].substr(64);
7350  }
7351 
7353  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[2], oa_prompter))
7354  {
7355  fail_msg_writer() << tr("failed to parse address");
7356  return true;
7357  }
7358 
7359  try
7360  {
7361  uint64_t received;
7362  bool in_pool;
7363  uint64_t confirmations;
7364  m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
7365 
7366  if (received > 0)
7367  {
7368  success_msg_writer() << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address) << " " << tr("received") << " " << print_etn(received) << " " << tr("in txid") << " " << txid;
7369  if (in_pool)
7370  {
7371  success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
7372  }
7373  else
7374  {
7375  if (confirmations != (uint64_t)-1)
7376  {
7377  success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
7378  }
7379  else
7380  {
7381  success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
7382  }
7383  }
7384  }
7385  else
7386  {
7387  fail_msg_writer() << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid;
7388  }
7389  }
7390  catch (const std::exception &e)
7391  {
7392  fail_msg_writer() << tr("error: ") << e.what();
7393  }
7394  return true;
7395 }
7396 //----------------------------------------------------------------------------------------------------
7397 bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
7398 {
7399  if(args.size() != 3 && args.size() != 4) {
7400  PRINT_USAGE(USAGE_CHECK_TX_PROOF);
7401  return true;
7402  }
7403 
7404  if (!try_connect_to_daemon())
7405  return true;
7406 
7407  // parse txid
7408  crypto::hash txid;
7409  if(!epee::string_tools::hex_to_pod(args[0], txid))
7410  {
7411  fail_msg_writer() << tr("failed to parse txid");
7412  return true;
7413  }
7414 
7415  // parse address
7417  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[1], oa_prompter))
7418  {
7419  fail_msg_writer() << tr("failed to parse address");
7420  return true;
7421  }
7422 
7423  // read signature file
7424  std::string sig_str;
7425  if (!epee::file_io_utils::load_file_to_string(args[2], sig_str))
7426  {
7427  fail_msg_writer() << tr("failed to load signature file");
7428  return true;
7429  }
7430 
7431  try
7432  {
7433  uint64_t received;
7434  bool in_pool;
7435  uint64_t confirmations;
7436  if (m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, args.size() == 4 ? args[3] : "", sig_str, received, in_pool, confirmations))
7437  {
7438  success_msg_writer() << tr("Good signature");
7439  if (received > 0)
7440  {
7441  success_msg_writer() << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address) << " " << tr("received") << " " << print_etn(received) << " " << tr("in txid") << " " << txid;
7442  if (in_pool)
7443  {
7444  success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
7445  }
7446  else
7447  {
7448  if (confirmations != (uint64_t)-1)
7449  {
7450  success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
7451  }
7452  else
7453  {
7454  success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
7455  }
7456  }
7457  }
7458  else
7459  {
7460  fail_msg_writer() << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid;
7461  }
7462  }
7463  else
7464  {
7465  fail_msg_writer() << tr("Bad signature");
7466  }
7467  }
7468  catch (const std::exception &e)
7469  {
7470  fail_msg_writer() << tr("error: ") << e.what();
7471  }
7472  return true;
7473 }
7474 //----------------------------------------------------------------------------------------------------
7475 bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
7476 {
7477  if (m_wallet->key_on_device())
7478  {
7479  fail_msg_writer() << tr("command not supported by HW wallet");
7480  return true;
7481  }
7482  if(args.size() != 1 && args.size() != 2) {
7483  PRINT_USAGE(USAGE_GET_SPEND_PROOF);
7484  return true;
7485  }
7486 
7487  if (m_wallet->watch_only())
7488  {
7489  fail_msg_writer() << tr("wallet is watch-only and cannot generate the proof");
7490  return true;
7491  }
7492 
7493  crypto::hash txid;
7494  if (!epee::string_tools::hex_to_pod(args[0], txid))
7495  {
7496  fail_msg_writer() << tr("failed to parse txid");
7497  return true;
7498  }
7499 
7500  if (!try_connect_to_daemon())
7501  return true;
7502 
7504 
7505  try
7506  {
7507  const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] : "");
7508  const std::string filename = "electroneum_spend_proof";
7509  if (epee::file_io_utils::save_string_to_file(filename, sig_str))
7510  success_msg_writer() << tr("signature file saved to: ") << filename;
7511  else
7512  fail_msg_writer() << tr("failed to save signature file");
7513  }
7514  catch (const std::exception &e)
7515  {
7516  fail_msg_writer() << e.what();
7517  }
7518  return true;
7519 }
7520 //----------------------------------------------------------------------------------------------------
7521 bool simple_wallet::check_spend_proof(const std::vector<std::string> &args)
7522 {
7523  if(args.size() != 2 && args.size() != 3) {
7524  PRINT_USAGE(USAGE_CHECK_SPEND_PROOF);
7525  return true;
7526  }
7527 
7528  crypto::hash txid;
7529  if (!epee::string_tools::hex_to_pod(args[0], txid))
7530  {
7531  fail_msg_writer() << tr("failed to parse txid");
7532  return true;
7533  }
7534 
7535  if (!try_connect_to_daemon())
7536  return true;
7537 
7538  std::string sig_str;
7539  if (!epee::file_io_utils::load_file_to_string(args[1], sig_str))
7540  {
7541  fail_msg_writer() << tr("failed to load signature file");
7542  return true;
7543  }
7544 
7545  try
7546  {
7547  if (m_wallet->check_spend_proof(txid, args.size() == 3 ? args[2] : "", sig_str))
7548  success_msg_writer() << tr("Good signature");
7549  else
7550  fail_msg_writer() << tr("Bad signature");
7551  }
7552  catch (const std::exception& e)
7553  {
7554  fail_msg_writer() << e.what();
7555  }
7556  return true;
7557 }
7558 //----------------------------------------------------------------------------------------------------
7559 bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args) //todo: reserve proofs including v10 outs (give them the chainstate indexes)
7560 {
7561  if (m_wallet->key_on_device())
7562  {
7563  fail_msg_writer() << tr("command not supported by HW wallet");
7564  return true;
7565  }
7566  if(args.size() != 1 && args.size() != 2) {
7567  PRINT_USAGE(USAGE_GET_RESERVE_PROOF);
7568  return true;
7569  }
7570 
7571  if (m_wallet->watch_only() || m_wallet->multisig())
7572  {
7573  fail_msg_writer() << tr("The reserve proof can be generated only by a full wallet");
7574  return true;
7575  }
7576 
7577  boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
7578  if (args[0] != "all")
7579  {
7580  account_minreserve = std::pair<uint32_t, uint64_t>();
7581  account_minreserve->first = m_current_subaddress_account;
7582  if (!cryptonote::parse_amount(account_minreserve->second, args[0]))
7583  {
7584  fail_msg_writer() << tr("amount is wrong: ") << args[0];
7585  return true;
7586  }
7587  }
7588 
7589  if (!try_connect_to_daemon())
7590  return true;
7591 
7593 
7594  try
7595  {
7596  const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] : "");
7597  const std::string filename = "electroneum_reserve_proof";
7598  if (epee::file_io_utils::save_string_to_file(filename, sig_str))
7599  success_msg_writer() << tr("signature file saved to: ") << filename;
7600  else
7601  fail_msg_writer() << tr("failed to save signature file");
7602  }
7603  catch (const std::exception &e)
7604  {
7605  fail_msg_writer() << e.what();
7606  }
7607  return true;
7608 }
7609 //----------------------------------------------------------------------------------------------------
7610 bool simple_wallet::check_reserve_proof(const std::vector<std::string> &args)
7611 {
7612  if(args.size() != 2 && args.size() != 3) {
7613  PRINT_USAGE(USAGE_CHECK_RESERVE_PROOF);
7614  return true;
7615  }
7616 
7617  if (!try_connect_to_daemon())
7618  return true;
7619 
7621  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[0], oa_prompter))
7622  {
7623  fail_msg_writer() << tr("failed to parse address");
7624  return true;
7625  }
7626  if (info.is_subaddress)
7627  {
7628  fail_msg_writer() << tr("Address must not be a subaddress");
7629  return true;
7630  }
7631 
7632  std::string sig_str;
7633  if (!epee::file_io_utils::load_file_to_string(args[1], sig_str))
7634  {
7635  fail_msg_writer() << tr("failed to load signature file");
7636  return true;
7637  }
7638 
7639  LOCK_IDLE_SCOPE();
7640 
7641  try
7642  {
7643  uint64_t total, spent;
7644  if (m_wallet->check_reserve_proof(info.address, args.size() == 3 ? args[2] : "", sig_str, total, spent))
7645  {
7646  success_msg_writer() << boost::format(tr("Good signature -- total: %s, spent: %s, unspent: %s")) % print_etn(total) % print_etn(spent) % print_etn(total - spent);
7647  }
7648  else
7649  {
7650  fail_msg_writer() << tr("Bad signature");
7651  }
7652  }
7653  catch (const std::exception& e)
7654  {
7655  fail_msg_writer() << e.what();
7656  }
7657  return true;
7658 }
7659 //----------------------------------------------------------------------------------------------------
7660 static std::string get_human_readable_timespan(std::chrono::seconds seconds)
7661 {
7662  uint64_t ts = seconds.count();
7663  if (ts < 60)
7664  return std::to_string(ts) + sw::tr(" seconds");
7665  if (ts < 3600)
7666  return std::to_string((uint64_t)(ts / 60)) + sw::tr(" minutes");
7667  if (ts < 3600 * 24)
7668  return std::to_string((uint64_t)(ts / 3600)) + sw::tr(" hours");
7669  if (ts < 3600 * 24 * 30.5)
7670  return std::to_string((uint64_t)(ts / (3600 * 24))) + sw::tr(" days");
7671  if (ts < 3600 * 24 * 365.25)
7672  return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + sw::tr(" months");
7673  return sw::tr("a long time");
7674 }
7675 //----------------------------------------------------------------------------------------------------
7676 // mutates local_args as it parses and consumes arguments
7677 bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vector<transfer_view>& transfers)
7678 {
7679  bool in = true;
7680  bool out = true;
7681  bool pending = true;
7682  bool failed = true;
7683  bool pool = true;
7684  bool coinbase = true;
7685  bool pub_blockchain_migration = true;
7686  bool sc_migration = true;
7687  uint64_t min_height = 0;
7688  uint64_t max_height = (uint64_t)-1;
7689 
7690  // optional in/out selector
7691  if (local_args.size() > 0) {
7692  if (local_args[0] == "in" || local_args[0] == "incoming") {
7693  out = pending = failed = pub_blockchain_migration = sc_migration = false;
7694  local_args.erase(local_args.begin());
7695  }
7696  else if (local_args[0] == "out" || local_args[0] == "outgoing") { // migration txes still print out as part of outbound transactions
7697  in = pool = coinbase = false;
7698  local_args.erase(local_args.begin());
7699  }
7700  else if (local_args[0] == "pending") {
7701  in = out = failed = coinbase = pub_blockchain_migration = sc_migration = false;
7702  local_args.erase(local_args.begin());
7703  }
7704  else if (local_args[0] == "failed") {
7705  in = out = pending = pool = coinbase = pub_blockchain_migration = sc_migration = false;
7706  local_args.erase(local_args.begin());
7707  }
7708  else if (local_args[0] == "pool") {
7709  in = out = pending = failed = coinbase = pub_blockchain_migration = sc_migration = false;
7710  local_args.erase(local_args.begin());
7711  }
7712  else if (local_args[0] == "coinbase") {
7713  in = out = pending = failed = pool = pub_blockchain_migration = sc_migration = false;
7714  coinbase = true;
7715  local_args.erase(local_args.begin());
7716  }
7717  else if (local_args[0] == "migration") {
7718  in = out = pending = failed = pool = sc_migration = false;
7719  coinbase = true;
7720  local_args.erase(local_args.begin());
7721  }
7722  else if (local_args[0] == "sc_migration") {
7723  in = out = pending = failed = pool = pub_blockchain_migration = false;
7724  coinbase = true;
7725  local_args.erase(local_args.begin());
7726  }
7727  else if (local_args[0] == "all" || local_args[0] == "both") {
7728  local_args.erase(local_args.begin());
7729  }
7730  }
7731 
7732  // subaddr_index
7733  std::set<uint32_t> subaddr_indices;
7734  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
7735  {
7736  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
7737  return false;
7738  local_args.erase(local_args.begin());
7739  }
7740 
7741  // min height
7742  if (local_args.size() > 0 && local_args[0].find('=') == std::string::npos) {
7743  try {
7744  min_height = boost::lexical_cast<uint64_t>(local_args[0]);
7745  }
7746  catch (const boost::bad_lexical_cast &) {
7747  fail_msg_writer() << tr("bad min_height parameter:") << " " << local_args[0];
7748  return false;
7749  }
7750  local_args.erase(local_args.begin());
7751  }
7752 
7753  // max height
7754  if (local_args.size() > 0 && local_args[0].find('=') == std::string::npos) {
7755  try {
7756  max_height = boost::lexical_cast<uint64_t>(local_args[0]);
7757  }
7758  catch (const boost::bad_lexical_cast &) {
7759  fail_msg_writer() << tr("bad max_height parameter:") << " " << local_args[0];
7760  return false;
7761  }
7762  local_args.erase(local_args.begin());
7763  }
7764 
7765  const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
7766 
7767  if (in || coinbase) {
7768  std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
7769  m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7770  for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7771  const tools::wallet2::payment_details &pd = i->second;
7772  if (!pd.m_coinbase && !in)
7773  continue;
7774  std::string payment_id = string_tools::pod_to_hex(i->first);
7775  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7776  payment_id = payment_id.substr(0,16);
7777  std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
7778  std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
7779  const std::string type = pd.m_coinbase ? tr("block") : tr("in");
7780  const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
7781  std::string locked_msg = "unlocked";
7782  if (!unlocked)
7783  {
7784  locked_msg = "locked";
7785  const uint64_t unlock_time = pd.m_unlock_time;
7787  {
7788  uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + (m_wallet->use_fork_rules(8, 0) ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE));
7789  if (bh >= last_block_height)
7790  locked_msg = std::to_string(bh - last_block_height) + " blks";
7791  }
7792  else
7793  {
7794  uint64_t current_time = static_cast<uint64_t>(time(NULL));
7796  if (threshold < pd.m_unlock_time)
7797  locked_msg = get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
7798  }
7799  }
7800  transfers.push_back({
7801  type,
7802  pd.m_block_height,
7803  pd.m_timestamp,
7804  type,
7805  true,
7806  pd.m_amount,
7807  pd.m_tx_hash,
7808  payment_id,
7809  0,
7810  {{destination, pd.m_amount}},
7811  {pd.m_subaddr_index.minor},
7812  note,
7813  locked_msg
7814  });
7815  }
7816  }
7817 
7818  if (out) {
7819  //DEAL WITH REGULAR OUTBOUND TX BESIDES MIGRATION TX
7820  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
7821  m_wallet->get_payments_out(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7822  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7823  const tools::wallet2::confirmed_transfer_details &pd = i->second;
7824  uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
7825  uint64_t fee = pd.m_amount_in - pd.m_amount_out;
7826  std::vector<std::pair<std::string, uint64_t>> destinations;
7827  for (const auto &d: pd.m_dests) {
7828  destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
7829  }
7830  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
7831  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7832  payment_id = payment_id.substr(0,16);
7833  std::string note = m_wallet->get_tx_note(i->first);
7834  transfers.push_back({
7835  "out",
7836  pd.m_block_height,
7837  pd.m_timestamp,
7838  "out",
7839  true,
7840  pd.m_amount_in - change - fee,
7841  i->first,
7842  payment_id,
7843  fee,
7844  destinations,
7845  pd.m_subaddr_indices,
7846  note,
7847  "-"
7848  });
7849  }
7850  }
7851 
7852  if(pub_blockchain_migration) {
7853  // Private Public MIGRATION TXS
7854  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> migration_payments;
7855  m_wallet->get_payments_out_migration(migration_payments, min_height, max_height, m_current_subaddress_account,
7856  subaddr_indices);
7857  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = migration_payments.begin();
7858  i != migration_payments.end(); ++i) {
7859  const tools::wallet2::confirmed_transfer_details &pd = i->second;
7860  uint64_t change = pd.m_change == (uint64_t) -1 ? 0 : pd.m_change; // change may not be known
7861  uint64_t fee = pd.m_amount_in - pd.m_amount_out;
7862  std::vector<std::pair<std::string, uint64_t>> destinations;
7863  for (const auto &d: pd.m_dests) {
7864  destinations.push_back(
7865  {get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
7866  }
7867  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
7868  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7869  payment_id = payment_id.substr(0, 16);
7870  std::string note = m_wallet->get_tx_note(i->first);
7871  transfers.push_back({
7872  "migration",
7873  pd.m_block_height,
7874  pd.m_timestamp,
7875  "migration",
7876  true,
7877  pd.m_amount_in - change - fee,
7878  i->first,
7879  payment_id,
7880  fee,
7881  destinations,
7882  pd.m_subaddr_indices,
7883  note,
7884  "-"
7885  });
7886  }
7887  }
7888 
7889  if(sc_migration) {
7890  // SC MIGRATION TXS
7891  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> sc_migration_payments;
7892  m_wallet->get_payments_out_sc_migration(sc_migration_payments, min_height, max_height,
7893  m_current_subaddress_account, subaddr_indices);
7894  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = sc_migration_payments.begin();
7895  i != sc_migration_payments.end(); ++i) {
7896  const tools::wallet2::confirmed_transfer_details &pd = i->second;
7897  uint64_t change = pd.m_change == (uint64_t) -1 ? 0 : pd.m_change; // change may not be known
7898  uint64_t fee = pd.m_amount_in - pd.m_amount_out;
7899  std::vector<std::pair<std::string, uint64_t>> destinations;
7900  for (const auto &d: pd.m_dests) {
7901  destinations.push_back(
7902  {get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
7903  }
7904  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
7905  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7906  payment_id = payment_id.substr(0, 16);
7907  std::string note = m_wallet->get_tx_note(i->first);
7908  transfers.push_back({
7909  "sc_migration",
7910  pd.m_block_height,
7911  pd.m_timestamp,
7912  "sc_migration",
7913  true,
7914  pd.m_amount_in - change - fee,
7915  i->first,
7916  payment_id,
7917  fee,
7918  destinations,
7919  pd.m_subaddr_indices,
7920  note,
7921  "-"
7922  });
7923  }
7924  }
7925 
7926  if (pool) {
7927  try
7928  {
7929  m_in_manual_refresh.store(true, std::memory_order_relaxed);
7930  epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
7931 
7932  m_wallet->update_pool_state();
7933  std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
7934  m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
7935  for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7936  const tools::wallet2::payment_details &pd = i->second.m_pd;
7937  std::string payment_id = string_tools::pod_to_hex(i->first);
7938  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7939  payment_id = payment_id.substr(0,16);
7940  std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
7941  std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
7942  std::string double_spend_note;
7943  if (i->second.m_double_spend_seen)
7944  double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] ");
7945  if (i->second.m_nonexistent_utxo_seen)
7946  success_msg_writer() << tr("Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
7947  transfers.push_back({
7948  "pool",
7949  "pool",
7950  pd.m_timestamp,
7951  "in",
7952  false,
7953  pd.m_amount,
7954  pd.m_tx_hash,
7955  payment_id,
7956  0,
7957  {{destination, pd.m_amount}},
7958  {pd.m_subaddr_index.minor},
7959  note + double_spend_note,
7960  "locked"
7961  });
7962  }
7963  }
7964  catch (const std::exception& e)
7965  {
7966  fail_msg_writer() << "Failed to get pool state:" << e.what();
7967  }
7968  }
7969 
7970  // print unconfirmed last
7971  if (pending || failed) {
7972  std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
7973  m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account, subaddr_indices);
7974  for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
7975  const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
7976  uint64_t amount = pd.m_amount_in;
7977  uint64_t fee = amount - pd.m_amount_out;
7978  std::vector<std::pair<std::string, uint64_t>> destinations;
7979  for (const auto &d: pd.m_dests) {
7980  destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
7981  }
7982  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
7983  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7984  payment_id = payment_id.substr(0,16);
7985  std::string note = m_wallet->get_tx_note(i->first);
7987  if ((failed && is_failed) || (!is_failed && pending)) {
7988  transfers.push_back({
7989  (is_failed ? "failed" : "pending"),
7990  (is_failed ? "failed" : "pending"),
7991  pd.m_timestamp,
7992  "out",
7993  false,
7994  amount - pd.m_change - fee,
7995  i->first,
7996  payment_id,
7997  fee,
7998  destinations,
7999  pd.m_subaddr_indices,
8000  note,
8001  "-"
8002  });
8003  }
8004  }
8005  }
8006  // sort by block, then by timestamp (unconfirmed last)
8007  std::sort(transfers.begin(), transfers.end(), [](const transfer_view& a, const transfer_view& b) -> bool {
8008  if (a.confirmed && !b.confirmed)
8009  return true;
8010  if (a.block == b.block)
8011  return a.timestamp < b.timestamp;
8012  return a.block < b.block;
8013  });
8014 
8015  return true;
8016 }
8017 //----------------------------------------------------------------------------------------------------
8018 bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
8019 {
8020  std::vector<std::string> local_args = args_;
8021 
8022  if(local_args.size() > 6) {
8023  fail_msg_writer() << tr("usage: show_transfers [in|out|pub_blockchain_migration|sc_migration|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
8024  return true;
8025  }
8026 
8027  LOCK_IDLE_SCOPE();
8028 
8029  std::vector<transfer_view> all_transfers;
8030 
8031  if (!get_transfers(local_args, all_transfers))
8032  return true;
8033 
8034  PAUSE_READLINE();
8035 
8036  for (const auto& transfer : all_transfers)
8037  {
8038  const auto color = transfer.type == "failed" ? console_color_red : transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_magenta) : console_color_default;
8039 
8040  std::string destinations = "-";
8041  if (!transfer.outputs.empty())
8042  {
8043  destinations = "";
8044  for (const auto& output : transfer.outputs)
8045  {
8046  if (!destinations.empty())
8047  destinations += ", ";
8048  destinations += (transfer.direction == "in" ? output.first.substr(0, 6) : output.first) + ":" + print_etn(output.second);
8049  }
8050  }
8051 
8052  auto formatter = boost::format("%8.8llu %12.12s %8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s");
8053 
8054  message_writer(color, false) << formatter
8055  % transfer.block
8056  % transfer.direction
8057  % transfer.unlocked
8058  % tools::get_human_readable_timestamp(transfer.timestamp)
8059  % print_etn(transfer.amount)
8060  % string_tools::pod_to_hex(transfer.hash)
8061  % transfer.payment_id
8062  % print_etn(transfer.fee)
8063  % destinations
8064  % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](uint32_t i) { return std::to_string(i); }), ", ")
8065  % transfer.note;
8066  }
8067 
8068  return true;
8069 }
8070 //----------------------------------------------------------------------------------------------------
8071 bool simple_wallet::export_transfers(const std::vector<std::string>& args_)
8072 {
8073  std::vector<std::string> local_args = args_;
8074 
8075  if(local_args.size() > 7) {
8076  fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]");
8077  return true;
8078  }
8079 
8080  LOCK_IDLE_SCOPE();
8081 
8082  std::vector<transfer_view> all_transfers;
8083 
8084  // might consumes arguments in local_args
8085  if (!get_transfers(local_args, all_transfers))
8086  return true;
8087 
8088  // output filename
8089  std::string filename = (boost::format("output%u.csv") % m_current_subaddress_account).str();
8090  if (local_args.size() > 0 && local_args[0].substr(0, 7) == "output=")
8091  {
8092  filename = local_args[0].substr(7, -1);
8093  local_args.erase(local_args.begin());
8094  }
8095 
8096  std::ofstream file(filename);
8097 
8098  // header
8099  file <<
8100  boost::format("%8.8s,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s") %
8101  tr("block") % tr("direction") % tr("unlocked") % tr("timestamp") % tr("amount") % tr("running balance") % tr("hash") % tr("payment ID") % tr("fee") % tr("destination") % tr("amount") % tr("index") % tr("note")
8102  << std::endl;
8103 
8104  uint64_t running_balance = 0;
8105  auto formatter = boost::format("%8.8llu,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s");
8106 
8107  for (const auto& transfer : all_transfers)
8108  {
8109  // ignore unconfirmed transfers in running balance
8110  if (transfer.confirmed)
8111  {
8112  if (transfer.direction == "in" || transfer.direction == "block")
8113  running_balance += transfer.amount;
8114  else
8115  running_balance -= transfer.amount + transfer.fee;
8116  }
8117 
8118  file << formatter
8119  % transfer.block
8120  % transfer.direction
8121  % transfer.unlocked
8122  % tools::get_human_readable_timestamp(transfer.timestamp)
8123  % print_etn(transfer.amount)
8124  % print_etn(running_balance)
8125  % string_tools::pod_to_hex(transfer.hash)
8126  % transfer.payment_id
8127  % print_etn(transfer.fee)
8128  % (transfer.outputs.size() ? transfer.outputs[0].first : "-")
8129  % (transfer.outputs.size() ? print_etn(transfer.outputs[0].second) : "")
8130  % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](uint32_t i) { return std::to_string(i); }), ", ")
8131  % transfer.note
8132  << std::endl;
8133 
8134  for (size_t i = 1; i < transfer.outputs.size(); ++i)
8135  {
8136  file << formatter
8137  % ""
8138  % ""
8139  % ""
8140  % ""
8141  % ""
8142  % ""
8143  % ""
8144  % ""
8145  % ""
8146  % transfer.outputs[i].first
8147  % print_etn(transfer.outputs[i].second)
8148  % ""
8149  % ""
8150  << std::endl;
8151  }
8152  }
8153  file.close();
8154 
8155  success_msg_writer() << tr("CSV exported to ") << filename;
8156 
8157  return true;
8158 }
8159 //----------------------------------------------------------------------------------------------------
8160 bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
8161 {
8162  if(args_.size() > 3)
8163  {
8164  PRINT_USAGE(USAGE_UNSPENT_OUTPUTS);
8165  return true;
8166  }
8167  auto local_args = args_;
8168 
8169  std::set<uint32_t> subaddr_indices;
8170  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
8171  {
8172  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
8173  return true;
8174  local_args.erase(local_args.begin());
8175  }
8176 
8177  uint64_t min_amount = 0;
8178  uint64_t max_amount = std::numeric_limits<uint64_t>::max();
8179  if (local_args.size() > 0)
8180  {
8181  if (!cryptonote::parse_amount(min_amount, local_args[0]))
8182  {
8183  fail_msg_writer() << tr("amount is wrong: ") << local_args[0];
8184  return true;
8185  }
8186  local_args.erase(local_args.begin());
8187  if (local_args.size() > 0)
8188  {
8189  if (!cryptonote::parse_amount(max_amount, local_args[0]))
8190  {
8191  fail_msg_writer() << tr("amount is wrong: ") << local_args[0];
8192  return true;
8193  }
8194  local_args.erase(local_args.begin());
8195  }
8196  if (min_amount > max_amount)
8197  {
8198  fail_msg_writer() << tr("<min_amount> should be smaller than <max_amount>");
8199  return true;
8200  }
8201  }
8203  m_wallet->get_transfers(transfers);
8204  std::map<uint64_t, tools::wallet2::transfer_container> amount_to_tds;
8205  uint64_t min_height = std::numeric_limits<uint64_t>::max();
8206  uint64_t max_height = 0;
8207  uint64_t found_min_amount = std::numeric_limits<uint64_t>::max();
8208  uint64_t found_max_amount = 0;
8209  uint64_t count = 0;
8210  for (const auto& td : transfers)
8211  {
8212  uint64_t amount = td.amount();
8213  if (td.m_spent || amount < min_amount || amount > max_amount || td.m_subaddr_index.major != m_current_subaddress_account || (subaddr_indices.count(td.m_subaddr_index.minor) == 0 && !subaddr_indices.empty()))
8214  continue;
8215  amount_to_tds[amount].push_back(td);
8216  if (min_height > td.m_block_height) min_height = td.m_block_height;
8217  if (max_height < td.m_block_height) max_height = td.m_block_height;
8218  if (found_min_amount > amount) found_min_amount = amount;
8219  if (found_max_amount < amount) found_max_amount = amount;
8220  ++count;
8221  }
8222  if (amount_to_tds.empty())
8223  {
8224  success_msg_writer() << tr("There is no unspent output in the specified address");
8225  return true;
8226  }
8227  for (const auto& amount_tds : amount_to_tds)
8228  {
8229  auto& tds = amount_tds.second;
8230  success_msg_writer() << tr("\nAmount: ") << print_etn(amount_tds.first) << tr(", number of keys: ") << tds.size();
8231  for (size_t i = 0; i < tds.size(); )
8232  {
8233  std::ostringstream oss;
8234  for (size_t j = 0; j < 8 && i < tds.size(); ++i, ++j)
8235  oss << tds[i].m_block_height << tr(" ");
8236  success_msg_writer() << oss.str();
8237  }
8238  }
8240  << tr("\nMin block height: ") << min_height
8241  << tr("\nMax block height: ") << max_height
8242  << tr("\nMin amount found: ") << print_etn(found_min_amount)
8243  << tr("\nMax amount found: ") << print_etn(found_max_amount)
8244  << tr("\nTotal count: ") << count;
8245  const size_t histogram_height = 10;
8246  const size_t histogram_width = 50;
8247  double bin_size = (max_height - min_height + 1.0) / histogram_width;
8248  size_t max_bin_count = 0;
8249  std::vector<size_t> histogram(histogram_width, 0);
8250  for (const auto& amount_tds : amount_to_tds)
8251  {
8252  for (auto& td : amount_tds.second)
8253  {
8254  uint64_t bin_index = (td.m_block_height - min_height + 1) / bin_size;
8255  if (bin_index >= histogram_width)
8256  bin_index = histogram_width - 1;
8257  histogram[bin_index]++;
8258  if (max_bin_count < histogram[bin_index])
8259  max_bin_count = histogram[bin_index];
8260  }
8261  }
8262  for (size_t x = 0; x < histogram_width; ++x)
8263  {
8264  double bin_count = histogram[x];
8265  if (max_bin_count > histogram_height)
8266  bin_count *= histogram_height / (double)max_bin_count;
8267  if (histogram[x] > 0 && bin_count < 1.0)
8268  bin_count = 1.0;
8269  histogram[x] = bin_count;
8270  }
8271  std::vector<std::string> histogram_line(histogram_height, std::string(histogram_width, ' '));
8272  for (size_t y = 0; y < histogram_height; ++y)
8273  {
8274  for (size_t x = 0; x < histogram_width; ++x)
8275  {
8276  if (y < histogram[x])
8277  histogram_line[y][x] = '*';
8278  }
8279  }
8280  double count_per_star = max_bin_count / (double)histogram_height;
8281  if (count_per_star < 1)
8282  count_per_star = 1;
8284  << tr("\nBin size: ") << bin_size
8285  << tr("\nOutputs per *: ") << count_per_star;
8286  ostringstream histogram_str;
8287  histogram_str << tr("count\n ^\n");
8288  for (size_t y = histogram_height; y > 0; --y)
8289  histogram_str << tr(" |") << histogram_line[y - 1] << tr("|\n");
8290  histogram_str
8291  << tr(" +") << std::string(histogram_width, '-') << tr("+--> block height\n")
8292  << tr(" ^") << std::string(histogram_width - 2, ' ') << tr("^\n")
8293  << tr(" ") << min_height << std::string(histogram_width - 8, ' ') << max_height;
8294  success_msg_writer() << histogram_str.str();
8295  return true;
8296 }
8297 //----------------------------------------------------------------------------------------------------
8298 bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
8299 {
8300  uint64_t start_height = 0;
8301  ResetType reset_type = ResetSoft;
8302 
8303  if (!args_.empty())
8304  {
8305  if (args_[0] == "hard")
8306  {
8307  reset_type = ResetHard;
8308  }
8309  else if (args_[0] == "soft")
8310  {
8311  reset_type = ResetSoft;
8312  }
8313  else if (args_[0] == "keep_ki")
8314  {
8315  reset_type = ResetSoftKeepKI;
8316  }
8317  else
8318  {
8319  PRINT_USAGE(USAGE_RESCAN_BC);
8320  return true;
8321  }
8322 
8323  if (args_.size() > 1)
8324  {
8325  try
8326  {
8327  start_height = boost::lexical_cast<uint64_t>( args_[1] );
8328  }
8329  catch(const boost::bad_lexical_cast &)
8330  {
8331  start_height = 0;
8332  }
8333  }
8334  }
8335 
8336  if (reset_type == ResetHard)
8337  {
8338  message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
8339  message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
8340  std::string confirm = input_line(tr("Rescan anyway?"), true);
8341  if(!std::cin.eof())
8342  {
8343  if (!command_line::is_yes(confirm))
8344  return true;
8345  }
8346  }
8347 
8348  const uint64_t wallet_from_height = m_wallet->get_refresh_from_block_height();
8349  if (start_height > wallet_from_height)
8350  {
8351  message_writer() << tr("Warning: your restore height is higher than wallet restore height: ") << wallet_from_height;
8352  std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
8353  if(!std::cin.eof())
8354  {
8355  if (!command_line::is_yes(confirm))
8356  return true;
8357  }
8358  }
8359 
8360  return refresh_main(start_height, reset_type, true);
8361 }
8362 //----------------------------------------------------------------------------------------------------
8363 void simple_wallet::check_for_messages()
8364 {
8365  try
8366  {
8367  std::vector<mms::message> new_messages;
8368  bool new_message = get_message_store().check_for_messages(get_multisig_wallet_state(), new_messages);
8369  if (new_message)
8370  {
8371  message_writer(console_color_magenta, true) << tr("MMS received new message");
8372  list_mms_messages(new_messages);
8373  m_cmd_binder.print_prompt();
8374  }
8375  }
8376  catch(...) {}
8377 }
8378 //----------------------------------------------------------------------------------------------------
8379 void simple_wallet::wallet_idle_thread()
8380 {
8381  while (true)
8382  {
8383  boost::unique_lock<boost::mutex> lock(m_idle_mutex);
8384  if (!m_idle_run.load(std::memory_order_relaxed))
8385  break;
8386 
8387  // auto refresh
8388  if (m_auto_refresh_enabled)
8389  {
8390  m_auto_refresh_refreshing = true;
8391  try
8392  {
8393  uint64_t fetched_blocks;
8394  bool received_etn;
8395  if (try_connect_to_daemon(true))
8396  m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, fetched_blocks, received_etn, false); // don't check the pool in background mode
8397  }
8398  catch(...) {}
8399  m_auto_refresh_refreshing = false;
8400  }
8401 
8402  // Check for new MMS messages;
8403  // For simplicity auto message check is ALSO controlled by "m_auto_refresh_enabled" and has no
8404  // separate thread either; thread syncing is tricky enough with only this one idle thread here
8405  if (m_auto_refresh_enabled && get_message_store().get_active())
8406  {
8407  check_for_messages();
8408  }
8409 
8410  if (!m_idle_run.load(std::memory_order_relaxed))
8411  break;
8412  m_idle_cond.wait_for(lock, boost::chrono::seconds(90));
8413  }
8414 }
8415 //----------------------------------------------------------------------------------------------------
8416 std::string simple_wallet::get_prompt() const
8417 {
8418  std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
8419  std::string prompt = std::string("[") + tr("wallet") + " " + addr_start;
8420  if (!m_wallet->check_connection(NULL))
8421  prompt += tr(" (no daemon)");
8422  else if (!m_wallet->is_synced())
8423  prompt += tr(" (out of sync)");
8424  prompt += "]: ";
8425  return prompt;
8426 }
8427 //----------------------------------------------------------------------------------------------------
8429 {
8430  // check and display warning, but go on anyway
8431  try_connect_to_daemon();
8432 
8433  //initial refresh
8434  refresh_main(0, ResetNone, true);
8435 
8436  m_auto_refresh_enabled = m_wallet->auto_refresh();
8437 
8438  // Idle thread which scans if m_auto_refresh_enabled, and listens to daemon
8439  m_idle_thread = boost::thread([&]{wallet_idle_thread();});
8440 
8441  message_writer(console_color_green, false) << "Background refresh thread started";
8442 
8443  //Indefinitely runs and listens on commands until user says to close wallet or an exception is thrown
8444  return m_cmd_binder.run_handling([this](){return get_prompt();}, "");
8445 }
8446 //----------------------------------------------------------------------------------------------------
8448 {
8449  m_cmd_binder.stop_handling();
8450 }
8451 //----------------------------------------------------------------------------------------------------
8452 bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8453 {
8454  // Usage:
8455  // account
8456  // account new <label text with white spaces allowed>
8457  // account switch <index>
8458  // account label <index> <label text with white spaces allowed>
8459  // account tag <tag_name> <account_index_1> [<account_index_2> ...]
8460  // account untag <account_index_1> [<account_index_2> ...]
8461  // account tag_description <tag_name> <description>
8462 
8463  if (args.empty())
8464  {
8465  // print all the existing accounts
8466  LOCK_IDLE_SCOPE();
8467  print_accounts();
8468  return true;
8469  }
8470 
8471  std::vector<std::string> local_args = args;
8472  std::string command = local_args[0];
8473  local_args.erase(local_args.begin());
8474  if (command == "new")
8475  {
8476  // create a new account and switch to it
8477  std::string label = boost::join(local_args, " ");
8478  if (label.empty())
8479  label = tr("(Untitled account)");
8480  m_wallet->add_subaddress_account(label);
8481  m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8482  // update_prompt();
8483  LOCK_IDLE_SCOPE();
8484  print_accounts();
8485  }
8486  else if(command == "generate" && local_args.size() == 1)
8487  {
8488  uint64_t count;
8489  if (!epee::string_tools::get_xtype_from_string(count, local_args[0]))
8490  {
8491  fail_msg_writer() << tr("failed to parse count: ") << local_args[0];
8492  return true;
8493  }
8494  if(count <= 0) {
8495  fail_msg_writer() << tr("specify a count higher than 0");
8496  return true;
8497  }
8498 
8499  try
8500  {
8501  uint64_t startAccountsCount = m_wallet->get_num_subaddress_accounts();
8502  // create N accounts
8503  for(uint64_t i = 0; i < count-1; i++) {
8504  m_wallet->add_subaddress_account("(Untitled account " + std::to_string(startAccountsCount + i+1) + " )", false);
8505 
8506  if(i % 1000 == 0) {
8507  success_msg_writer() << "Generating Accounts... (" << startAccountsCount + i+1 << "/" << startAccountsCount+count << ")";
8508  }
8509  }
8510  m_wallet->add_subaddress_account("(Untitled account " + std::to_string(startAccountsCount + count) + " )", true);
8511  success_msg_writer() << "Generating Accounts... (" << startAccountsCount + count << "/" << startAccountsCount+count << ")";
8512  m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8513  }
8514  catch (const std::exception& e)
8515  {
8516  fail_msg_writer() << e.what();
8517  }
8518 
8519  }
8520  else if (command == "switch" && local_args.size() == 1)
8521  {
8522  // switch to the specified account
8523  uint32_t index_major;
8524  if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0]))
8525  {
8526  fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
8527  return true;
8528  }
8529  if (index_major >= m_wallet->get_num_subaddress_accounts())
8530  {
8531  fail_msg_writer() << tr("specify an index between 0 and ") << (m_wallet->get_num_subaddress_accounts() - 1);
8532  return true;
8533  }
8534  m_current_subaddress_account = index_major;
8535  // update_prompt();
8536  show_balance();
8537  }
8538  else if (command == "label" && local_args.size() >= 1)
8539  {
8540  // set label of the specified account
8541  uint32_t index_major;
8542  if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0]))
8543  {
8544  fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
8545  return true;
8546  }
8547  local_args.erase(local_args.begin());
8548  std::string label = boost::join(local_args, " ");
8549  try
8550  {
8551  m_wallet->set_subaddress_label({index_major, 0}, label);
8552  LOCK_IDLE_SCOPE();
8553  print_accounts();
8554  }
8555  catch (const std::exception& e)
8556  {
8557  fail_msg_writer() << e.what();
8558  }
8559  }
8560  else if (command == "tag" && local_args.size() >= 2)
8561  {
8562  const std::string tag = local_args[0];
8563  std::set<uint32_t> account_indices;
8564  for (size_t i = 1; i < local_args.size(); ++i)
8565  {
8566  uint32_t account_index;
8567  if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
8568  {
8569  fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
8570  return true;
8571  }
8572  account_indices.insert(account_index);
8573  }
8574  try
8575  {
8576  m_wallet->set_account_tag(account_indices, tag);
8577  print_accounts(tag);
8578  }
8579  catch (const std::exception& e)
8580  {
8581  fail_msg_writer() << e.what();
8582  }
8583  }
8584  else if (command == "untag" && local_args.size() >= 1)
8585  {
8586  std::set<uint32_t> account_indices;
8587  for (size_t i = 0; i < local_args.size(); ++i)
8588  {
8589  uint32_t account_index;
8590  if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
8591  {
8592  fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
8593  return true;
8594  }
8595  account_indices.insert(account_index);
8596  }
8597  try
8598  {
8599  m_wallet->set_account_tag(account_indices, "");
8600  print_accounts();
8601  }
8602  catch (const std::exception& e)
8603  {
8604  fail_msg_writer() << e.what();
8605  }
8606  }
8607  else if (command == "tag_description" && local_args.size() >= 1)
8608  {
8609  const std::string tag = local_args[0];
8610  std::string description;
8611  if (local_args.size() > 1)
8612  {
8613  local_args.erase(local_args.begin());
8614  description = boost::join(local_args, " ");
8615  }
8616  try
8617  {
8618  m_wallet->set_account_tag_description(tag, description);
8619  print_accounts(tag);
8620  }
8621  catch (const std::exception& e)
8622  {
8623  fail_msg_writer() << e.what();
8624  }
8625  }
8626  else
8627  {
8628  PRINT_USAGE(USAGE_ACCOUNT);
8629  }
8630  return true;
8631 }
8632 //----------------------------------------------------------------------------------------------------
8633 void simple_wallet::print_accounts()
8634 {
8635  const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8636  size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
8637  for (const std::pair<std::string, std::string>& p : account_tags.first)
8638  {
8639  const std::string& tag = p.first;
8640  print_accounts(tag);
8641  num_untagged_accounts -= std::count(account_tags.second.begin(), account_tags.second.end(), tag);
8642  success_msg_writer() << "";
8643  }
8644 
8645  if (num_untagged_accounts > 0)
8646  print_accounts("");
8647 
8648  if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) {
8649  if (m_wallet->balance_all(false)) { //if they still have old outs, let them know
8650  success_msg_writer() << tr("\nGrand total:\n Pre V10 Balance: ") << print_etn(m_wallet->balance_all(false))
8651  << tr(", pre v10 unlocked balance: ")
8652  << print_etn(m_wallet->unlocked_balance_all(false));
8653  }
8654  success_msg_writer() << tr("\nGrand total:\n Balance: ") << print_etn(m_wallet->balance_all(true))
8655  << tr(", unlocked balance: ") << print_etn(m_wallet->unlocked_balance_all(true));
8656  }
8657  }
8658 //----------------------------------------------------------------------------------------------------
8659 void simple_wallet::print_accounts(const std::string& tag)
8660 {
8661  const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8662  if (tag.empty())
8663  {
8664  success_msg_writer() << tr("Untagged accounts:");
8665  }
8666  else
8667  {
8668  if (account_tags.first.count(tag) == 0)
8669  {
8670  fail_msg_writer() << boost::format(tr("Tag %s is unregistered.")) % tag;
8671  return;
8672  }
8673  success_msg_writer() << tr("Accounts with tag: ") << tag;
8674  success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
8675  }
8676 
8677  uint64_t total_balance = 0, total_unlocked_balance = 0, total_balance_public_chain = 0, total_unlocked_balance_public_chain = 0;
8678  bool found_old_bal = false;
8679  //quick check to see if there is *any* pre v10 balance. todo: Refactor another time for performance.
8680  for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8681  if (account_tags.second[account_index] != tag)
8682  continue;
8683  if (m_wallet->balance(account_index, false)) {
8684  found_old_bal = true;
8685  break;
8686  }
8687  }
8688 
8689  if (found_old_bal) {
8690  success_msg_writer() << "Pre V10 Account Balances";
8691  success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr(" Pre V10 Balance") %
8692  tr("Pre V10 Unlocked balance") % tr("Label");
8693  for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8694  if (account_tags.second[account_index] != tag)
8695  continue;
8696  success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
8697  % (m_current_subaddress_account == account_index ? '*' : ' ')
8698  % account_index
8699  % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8700  % print_etn(m_wallet->balance(account_index, false))
8701  % print_etn(m_wallet->unlocked_balance(account_index, false))
8702  % m_wallet->get_subaddress_label({account_index, 0});
8703  total_balance += m_wallet->balance(account_index, false);
8704  total_unlocked_balance += m_wallet->unlocked_balance(account_index, false);
8705  }
8707  << tr("----------------------------------------------------------------------------------");
8708  success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_etn(total_balance) %
8709  print_etn(total_unlocked_balance);
8710  success_msg_writer() << "";
8711  }
8712 
8713  //new balances
8714  if(m_wallet->synced_to_v10()) {
8715  success_msg_writer() << "Account Balances";
8716  success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") %
8717  tr("Unlocked balance") % tr("Label");
8718  for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8719  if (account_tags.second[account_index] != tag)
8720  continue;
8721  success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
8722  % (m_current_subaddress_account == account_index ? '*' : ' ')
8723  % account_index
8724  % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8725  % print_etn(m_wallet->balance(account_index, true))
8726  % print_etn(m_wallet->unlocked_balance(account_index, true))
8727  % m_wallet->get_subaddress_label({account_index, 0});
8728  total_balance_public_chain += m_wallet->balance(account_index, true);
8729  total_unlocked_balance_public_chain += m_wallet->unlocked_balance(account_index, true);
8730  }
8732  << tr("----------------------------------------------------------------------------------");
8733  success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_etn(total_balance_public_chain) %
8734  print_etn(total_unlocked_balance_public_chain);
8735  }
8736 
8737 }
8738 //----------------------------------------------------------------------------------------------------
8739 bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8740 {
8741  // Usage:
8742  // address
8743  // address new <label text with white spaces allowed>
8744  // address all
8745  // address <index_min> [<index_max>]
8746  // address label <index> <label text with white spaces allowed>
8747 
8748  std::vector<std::string> local_args = args;
8750  m_wallet->get_transfers(transfers);
8751 
8752  auto print_address_sub = [this, &transfers](uint32_t index)
8753  {
8754  bool used = std::find_if(
8755  transfers.begin(), transfers.end(),
8756  [this, &index](const tools::wallet2::transfer_details& td) {
8757  return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
8758  }) != transfers.end();
8759  success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({m_current_subaddress_account, index}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
8760  };
8761 
8762  uint32_t index = 0;
8763  if (local_args.empty())
8764  {
8765  print_address_sub(index);
8766  }
8767  else if (local_args.size() == 1 && local_args[0] == "all")
8768  {
8769  local_args.erase(local_args.begin());
8770  for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
8771  print_address_sub(index);
8772  }
8773  else if (local_args[0] == "new")
8774  {
8775  local_args.erase(local_args.begin());
8776  std::string label;
8777  if (local_args.size() > 0)
8778  label = boost::join(local_args, " ");
8779  if (label.empty())
8780  label = tr("(Untitled address)");
8781  m_wallet->add_subaddress(m_current_subaddress_account, label);
8782  print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8783  }
8784  else if (local_args.size() >= 2 && local_args[0] == "label")
8785  {
8786  if (!epee::string_tools::get_xtype_from_string(index, local_args[1]))
8787  {
8788  fail_msg_writer() << tr("failed to parse index: ") << local_args[1];
8789  return true;
8790  }
8791  if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8792  {
8793  fail_msg_writer() << tr("specify an index between 0 and ") << (m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8794  return true;
8795  }
8796  local_args.erase(local_args.begin());
8797  local_args.erase(local_args.begin());
8798  std::string label = boost::join(local_args, " ");
8799  m_wallet->set_subaddress_label({m_current_subaddress_account, index}, label);
8800  print_address_sub(index);
8801  }
8802  else if (local_args.size() <= 2 && epee::string_tools::get_xtype_from_string(index, local_args[0]))
8803  {
8804  local_args.erase(local_args.begin());
8805  uint32_t index_min = index;
8806  uint32_t index_max = index_min;
8807  if (local_args.size() > 0)
8808  {
8809  if (!epee::string_tools::get_xtype_from_string(index_max, local_args[0]))
8810  {
8811  fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
8812  return true;
8813  }
8814  local_args.erase(local_args.begin());
8815  }
8816  if (index_max < index_min)
8817  std::swap(index_min, index_max);
8818  if (index_min >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8819  {
8820  fail_msg_writer() << tr("<index_min> is already out of bound");
8821  return true;
8822  }
8823  if (index_max >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8824  {
8825  message_writer() << tr("<index_max> exceeds the bound");
8826  index_max = m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1;
8827  }
8828  for (index = index_min; index <= index_max; ++index)
8829  print_address_sub(index);
8830  }
8831  else
8832  {
8833  PRINT_USAGE(USAGE_ADDRESS);
8834  }
8835 
8836  return true;
8837 }
8838 //----------------------------------------------------------------------------------------------------
8839 bool simple_wallet::print_integrated_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8840 {
8841  crypto::hash8 payment_id;
8842  if (args.size() > 1)
8843  {
8844  PRINT_USAGE(USAGE_INTEGRATED_ADDRESS);
8845  return true;
8846  }
8847  if (args.size() == 0)
8848  {
8849  if (m_current_subaddress_account != 0)
8850  {
8851  fail_msg_writer() << tr("Integrated addresses can only be created for account 0");
8852  return true;
8853  }
8854  payment_id = crypto::rand<crypto::hash8>();
8855  success_msg_writer() << tr("Random payment ID: ") << payment_id;
8856  success_msg_writer() << tr("Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8857  return true;
8858  }
8859  if(tools::wallet2::parse_short_payment_id(args.back(), payment_id))
8860  {
8861  if (m_current_subaddress_account != 0)
8862  {
8863  fail_msg_writer() << tr("Integrated addresses can only be created for account 0");
8864  return true;
8865  }
8866  success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8867  return true;
8868  }
8869  else {
8871  if(get_account_address_from_str(info, m_wallet->nettype(), args.back()))
8872  {
8873  if (info.has_payment_id)
8874  {
8875  success_msg_writer() << boost::format(tr("Integrated address: %s, payment ID: %s")) %
8876  get_account_address_as_str(m_wallet->nettype(), false, info.address) % epee::string_tools::pod_to_hex(info.payment_id);
8877  }
8878  else
8879  {
8880  success_msg_writer() << (info.is_subaddress ? tr("Subaddress: ") : tr("Standard address: ")) << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
8881  }
8882  return true;
8883  }
8884  }
8885  fail_msg_writer() << tr("failed to parse payment ID or address");
8886  return true;
8887 }
8888 //----------------------------------------------------------------------------------------------------
8889 bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8890 {
8891  if (args.size() == 0)
8892  {
8893  }
8894  else if (args.size() == 1 || (args[0] != "add" && args[0] != "delete"))
8895  {
8896  PRINT_USAGE(USAGE_ADDRESS_BOOK);
8897  return true;
8898  }
8899  else if (args[0] == "add")
8900  {
8902  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[1], oa_prompter))
8903  {
8904  fail_msg_writer() << tr("failed to parse address");
8905  return true;
8906  }
8907  crypto::hash payment_id = crypto::null_hash;
8908  size_t description_start = 2;
8909  if (info.has_payment_id)
8910  {
8911  memcpy(payment_id.data, info.payment_id.data, 8);
8912  }
8913  else if (!info.has_payment_id && args.size() >= 4 && args[2] == "pid")
8914  {
8915  if (tools::wallet2::parse_long_payment_id(args[3], payment_id))
8916  {
8918  description_start += 2;
8919  }
8920  else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id))
8921  {
8922  fail_msg_writer() << tr("Short payment IDs are to be used within an integrated address only");
8923  return true;
8924  }
8925  else
8926  {
8927  fail_msg_writer() << tr("failed to parse payment ID");
8928  return true;
8929  }
8930  }
8931  std::string description;
8932  for (size_t i = description_start; i < args.size(); ++i)
8933  {
8934  if (i > description_start)
8935  description += " ";
8936  description += args[i];
8937  }
8938  m_wallet->add_address_book_row(info.address, payment_id, description, info.is_subaddress);
8939  }
8940  else
8941  {
8942  size_t row_id;
8943  if(!epee::string_tools::get_xtype_from_string(row_id, args[1]))
8944  {
8945  fail_msg_writer() << tr("failed to parse index");
8946  return true;
8947  }
8948  m_wallet->delete_address_book_row(row_id);
8949  }
8950  auto address_book = m_wallet->get_address_book();
8951  if (address_book.empty())
8952  {
8953  success_msg_writer() << tr("Address book is empty.");
8954  }
8955  else
8956  {
8957  for (size_t i = 0; i < address_book.size(); ++i) {
8958  auto& row = address_book[i];
8959  success_msg_writer() << tr("Index: ") << i;
8960  success_msg_writer() << tr("Address: ") << get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
8961  success_msg_writer() << tr("Payment ID: ") << row.m_payment_id << " (OBSOLETE)";
8962  success_msg_writer() << tr("Description: ") << row.m_description << "\n";
8963  }
8964  }
8965  return true;
8966 }
8967 //----------------------------------------------------------------------------------------------------
8968 bool simple_wallet::set_tx_note(const std::vector<std::string> &args)
8969 {
8970  if (args.size() == 0)
8971  {
8972  PRINT_USAGE(USAGE_SET_TX_NOTE);
8973  return true;
8974  }
8975 
8976  cryptonote::blobdata txid_data;
8977  if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
8978  {
8979  fail_msg_writer() << tr("failed to parse txid");
8980  return true;
8981  }
8982  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
8983 
8984  std::string note = "";
8985  for (size_t n = 1; n < args.size(); ++n)
8986  {
8987  if (n > 1)
8988  note += " ";
8989  note += args[n];
8990  }
8991  m_wallet->set_tx_note(txid, note);
8992 
8993  return true;
8994 }
8995 //----------------------------------------------------------------------------------------------------
8996 bool simple_wallet::get_tx_note(const std::vector<std::string> &args)
8997 {
8998  if (args.size() != 1)
8999  {
9000  PRINT_USAGE(USAGE_GET_TX_NOTE);
9001  return true;
9002  }
9003 
9004  cryptonote::blobdata txid_data;
9005  if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
9006  {
9007  fail_msg_writer() << tr("failed to parse txid");
9008  return true;
9009  }
9010  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
9011 
9012  std::string note = m_wallet->get_tx_note(txid);
9013  if (note.empty())
9014  success_msg_writer() << "no note found";
9015  else
9016  success_msg_writer() << "note found: " << note;
9017 
9018  return true;
9019 }
9020 //----------------------------------------------------------------------------------------------------
9021 bool simple_wallet::set_description(const std::vector<std::string> &args)
9022 {
9023  // 0 arguments allowed, for setting the description to empty string
9024 
9025  std::string description = "";
9026  for (size_t n = 0; n < args.size(); ++n)
9027  {
9028  if (n > 0)
9029  description += " ";
9030  description += args[n];
9031  }
9032  m_wallet->set_description(description);
9033 
9034  return true;
9035 }
9036 //----------------------------------------------------------------------------------------------------
9037 bool simple_wallet::get_description(const std::vector<std::string> &args)
9038 {
9039  if (args.size() != 0)
9040  {
9041  PRINT_USAGE(USAGE_GET_DESCRIPTION);
9042  return true;
9043  }
9044 
9045  std::string description = m_wallet->get_description();
9046  if (description.empty())
9047  success_msg_writer() << tr("no description found");
9048  else
9049  success_msg_writer() << tr("description found: ") << description;
9050 
9051  return true;
9052 }
9053 //----------------------------------------------------------------------------------------------------
9054 bool simple_wallet::status(const std::vector<std::string> &args)
9055 {
9056  uint64_t local_height = m_wallet->get_blockchain_current_height();
9057  uint32_t version = 0;
9058  bool ssl = false;
9059  if (!m_wallet->check_connection(&version, &ssl))
9060  {
9061  success_msg_writer() << "Refreshed " << local_height << "/?, no daemon connected";
9062  return true;
9063  }
9064 
9065  std::string err;
9066  uint64_t bc_height = get_daemon_blockchain_height(err);
9067  if (err.empty())
9068  {
9069  bool synced = local_height == bc_height;
9070  success_msg_writer() << "Refreshed " << local_height << "/" << bc_height << ", " << (synced ? "synced" : "syncing")
9071  << ", daemon RPC v" << get_version_string(version) << ", " << (ssl ? "SSL" : "no SSL");
9072  }
9073  else
9074  {
9075  fail_msg_writer() << "Refreshed " << local_height << "/?, daemon connection error";
9076  }
9077  return true;
9078 }
9079 //----------------------------------------------------------------------------------------------------
9080 bool simple_wallet::wallet_info(const std::vector<std::string> &args)
9081 {
9082  bool ready;
9083  uint32_t threshold, total;
9084  std::string description = m_wallet->get_description();
9085  if (description.empty())
9086  {
9087  description = "<Not set>";
9088  }
9089  message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
9090  message_writer() << tr("Description: ") << description;
9091  message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
9092  std::string type;
9093  if (m_wallet->watch_only())
9094  type = tr("Watch only");
9095  else if (m_wallet->multisig(&ready, &threshold, &total))
9096  type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str();
9097  else
9098  type = tr("Normal");
9099  message_writer() << tr("Type: ") << type;
9100  message_writer() << tr("Network type: ") << (
9101  m_wallet->nettype() == cryptonote::TESTNET ? tr("Testnet") :
9102  m_wallet->nettype() == cryptonote::STAGENET ? tr("Stagenet") : tr("Mainnet"));
9103  return true;
9104 }
9105 //----------------------------------------------------------------------------------------------------
9106 bool simple_wallet::sign(const std::vector<std::string> &args)
9107 {
9108  if (m_wallet->key_on_device())
9109  {
9110  fail_msg_writer() << tr("command not supported by HW wallet");
9111  return true;
9112  }
9113  if (args.size() != 1)
9114  {
9115  PRINT_USAGE(USAGE_SIGN);
9116  return true;
9117  }
9118  if (m_wallet->watch_only())
9119  {
9120  fail_msg_writer() << tr("wallet is watch-only and cannot sign");
9121  return true;
9122  }
9123  if (m_wallet->multisig())
9124  {
9125  fail_msg_writer() << tr("This wallet is multisig and cannot sign");
9126  return true;
9127  }
9128 
9129  std::string filename = args[0];
9130  std::string data;
9131  bool r = epee::file_io_utils::load_file_to_string(filename, data);
9132  if (!r)
9133  {
9134  fail_msg_writer() << tr("failed to read file ") << filename;
9135  return true;
9136  }
9137 
9139 
9140  std::string signature = m_wallet->sign(data);
9142  return true;
9143 }
9144 //----------------------------------------------------------------------------------------------------
9145 bool simple_wallet::verify(const std::vector<std::string> &args)
9146 {
9147  if (args.size() != 3)
9148  {
9149  PRINT_USAGE(USAGE_VERIFY);
9150  return true;
9151  }
9152  std::string filename = args[0];
9153  std::string address_string = args[1];
9154  std::string signature= args[2];
9155 
9156  std::string data;
9157  bool r = epee::file_io_utils::load_file_to_string(filename, data);
9158  if (!r)
9159  {
9160  fail_msg_writer() << tr("failed to read file ") << filename;
9161  return true;
9162  }
9163 
9165  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_string, oa_prompter))
9166  {
9167  fail_msg_writer() << tr("failed to parse address");
9168  return true;
9169  }
9170 
9171  r = m_wallet->verify(data, info.address, signature);
9172  if (!r)
9173  {
9174  fail_msg_writer() << tr("Bad signature from ") << address_string;
9175  }
9176  else
9177  {
9178  success_msg_writer() << tr("Good signature from ") << address_string;
9179  }
9180  return true;
9181 }
9182 //----------------------------------------------------------------------------------------------------
9183 bool simple_wallet::export_key_images(const std::vector<std::string> &args)
9184 {
9185  if (m_wallet->key_on_device())
9186  {
9187  fail_msg_writer() << tr("command not supported by HW wallet");
9188  return true;
9189  }
9190  if (args.size() != 1)
9191  {
9192  PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES);
9193  return true;
9194  }
9195  if (m_wallet->watch_only())
9196  {
9197  fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
9198  return true;
9199  }
9200 
9201  std::string filename = args[0];
9202  if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9203  return true;
9204 
9206 
9207  try
9208  {
9209  if (!m_wallet->export_key_images(filename))
9210  {
9211  fail_msg_writer() << tr("failed to save file ") << filename;
9212  return true;
9213  }
9214  }
9215  catch (const std::exception &e)
9216  {
9217  LOG_ERROR("Error exporting key images: " << e.what());
9218  fail_msg_writer() << "Error exporting key images: " << e.what();
9219  return true;
9220  }
9221 
9222  success_msg_writer() << tr("Signed key images exported to ") << filename;
9223  return true;
9224 }
9225 //----------------------------------------------------------------------------------------------------
9226 bool simple_wallet::import_key_images(const std::vector<std::string> &args)
9227 {
9228  if (m_wallet->key_on_device())
9229  {
9230  fail_msg_writer() << tr("command not supported by HW wallet");
9231  return true;
9232  }
9233  if (!m_wallet->is_trusted_daemon())
9234  {
9235  fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
9236  return true;
9237  }
9238 
9239  if (args.size() != 1)
9240  {
9241  PRINT_USAGE(USAGE_IMPORT_KEY_IMAGES);
9242  return true;
9243  }
9244  std::string filename = args[0];
9245 
9246  LOCK_IDLE_SCOPE();
9247  try
9248  {
9249  uint64_t spent = 0, unspent = 0;
9250  uint64_t height = m_wallet->import_key_images(filename, spent, unspent);
9251  success_msg_writer() << "Signed key images imported to height " << height << ", "
9252  << print_etn(spent) << " spent, " << print_etn(unspent) << " unspent";
9253  }
9254  catch (const std::exception &e)
9255  {
9256  fail_msg_writer() << "Failed to import key images: " << e.what();
9257  return true;
9258  }
9259 
9260  return true;
9261 }
9262 //----------------------------------------------------------------------------------------------------
9263 bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
9264 {
9265  if (!m_wallet->key_on_device())
9266  {
9267  fail_msg_writer() << tr("command only supported by HW wallet");
9268  return true;
9269  }
9270  if (!m_wallet->get_account().get_device().has_ki_cold_sync())
9271  {
9272  fail_msg_writer() << tr("hw wallet does not support cold KI sync");
9273  return true;
9274  }
9275 
9276  LOCK_IDLE_SCOPE();
9277  key_images_sync_intern();
9278  return true;
9279 }
9280 //----------------------------------------------------------------------------------------------------
9281 void simple_wallet::key_images_sync_intern(){
9282  try
9283  {
9284  message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
9285 
9286  uint64_t spent = 0, unspent = 0;
9287  uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
9288  if (height > 0)
9289  {
9290  success_msg_writer() << tr("Key images synchronized to height ") << height;
9291  if (!m_wallet->is_trusted_daemon())
9292  {
9293  message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
9294  } else
9295  {
9296  success_msg_writer() << print_etn(spent) << tr(" spent, ") << print_etn(unspent) << tr(" unspent");
9297  }
9298  }
9299  else {
9300  fail_msg_writer() << tr("Failed to import key images");
9301  }
9302  }
9303  catch (const std::exception &e)
9304  {
9305  fail_msg_writer() << tr("Failed to import key images: ") << e.what();
9306  }
9307 }
9308 //----------------------------------------------------------------------------------------------------
9309 bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
9310 {
9311  if (!m_wallet->key_on_device())
9312  {
9313  fail_msg_writer() << tr("command only supported by HW wallet");
9314  return true;
9315  }
9316 
9317  LOCK_IDLE_SCOPE();
9318  try
9319  {
9320  bool r = m_wallet->reconnect_device();
9321  if (!r){
9322  fail_msg_writer() << tr("Failed to reconnect device");
9323  }
9324  }
9325  catch (const std::exception &e)
9326  {
9327  fail_msg_writer() << tr("Failed to reconnect device: ") << tr(e.what());
9328  return true;
9329  }
9330 
9331  return true;
9332 }
9333 //----------------------------------------------------------------------------------------------------
9334 bool simple_wallet::export_outputs(const std::vector<std::string> &args)
9335 {
9336  if (m_wallet->key_on_device())
9337  {
9338  fail_msg_writer() << tr("command not supported by HW wallet");
9339  return true;
9340  }
9341  if (args.size() != 1)
9342  {
9343  PRINT_USAGE(USAGE_EXPORT_OUTPUTS);
9344  return true;
9345  }
9346 
9347  std::string filename = args[0];
9348  if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9349  return true;
9350 
9352 
9353  try
9354  {
9355  std::string data = m_wallet->export_outputs_to_str();
9356  bool r = epee::file_io_utils::save_string_to_file(filename, data);
9357  if (!r)
9358  {
9359  fail_msg_writer() << tr("failed to save file ") << filename;
9360  return true;
9361  }
9362  }
9363  catch (const std::exception &e)
9364  {
9365  LOG_ERROR("Error exporting outputs: " << e.what());
9366  fail_msg_writer() << "Error exporting outputs: " << e.what();
9367  return true;
9368  }
9369 
9370  success_msg_writer() << tr("Outputs exported to ") << filename;
9371  return true;
9372 }
9373 //----------------------------------------------------------------------------------------------------
9374 bool simple_wallet::import_outputs(const std::vector<std::string> &args)
9375 {
9376  if (m_wallet->key_on_device())
9377  {
9378  fail_msg_writer() << tr("command not supported by HW wallet");
9379  return true;
9380  }
9381  if (args.size() != 1)
9382  {
9383  PRINT_USAGE(USAGE_IMPORT_OUTPUTS);
9384  return true;
9385  }
9386  std::string filename = args[0];
9387 
9388  std::string data;
9389  bool r = epee::file_io_utils::load_file_to_string(filename, data);
9390  if (!r)
9391  {
9392  fail_msg_writer() << tr("failed to read file ") << filename;
9393  return true;
9394  }
9395 
9396  try
9397  {
9399  size_t n_outputs = m_wallet->import_outputs_from_str(data);
9400  success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
9401  }
9402  catch (const std::exception &e)
9403  {
9404  fail_msg_writer() << "Failed to import outputs " << filename << ": " << e.what();
9405  return true;
9406  }
9407 
9408  return true;
9409 }
9410 //----------------------------------------------------------------------------------------------------
9411 bool simple_wallet::show_transfer(const std::vector<std::string> &args)
9412 {
9413  if (args.size() != 1)
9414  {
9415  PRINT_USAGE(USAGE_SHOW_TRANSFER);
9416  return true;
9417  }
9418 
9419  cryptonote::blobdata txid_data;
9420  if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
9421  {
9422  fail_msg_writer() << tr("failed to parse txid");
9423  return true;
9424  }
9425  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
9426 
9427  const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
9428 
9429  std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
9430  m_wallet->get_payments(payments, 0, (uint64_t)-1, m_current_subaddress_account);
9431  for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
9432  const tools::wallet2::payment_details &pd = i->second;
9433  if (pd.m_tx_hash == txid) {
9434  std::string payment_id = string_tools::pod_to_hex(i->first);
9435  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9436  payment_id = payment_id.substr(0,16);
9437  success_msg_writer() << "Incoming transaction found";
9438  success_msg_writer() << "txid: " << txid;
9439  success_msg_writer() << "Height: " << pd.m_block_height;
9441  success_msg_writer() << "Amount: " << print_etn(pd.m_amount);
9442  success_msg_writer() << "Payment ID: " << payment_id;
9444  {
9445  uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + (m_wallet->use_fork_rules(8, 0) ? ETN_DEFAULT_TX_SPENDABLE_AGE_V8 : CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE));
9446  uint64_t last_block_reward = m_wallet->get_last_block_reward();
9447  uint64_t suggested_threshold = last_block_reward ? (pd.m_amount + last_block_reward - 1) / last_block_reward : 0;
9448  if (bh >= last_block_height)
9449  success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock";
9450  else if (suggested_threshold > 0)
9451  success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations (" << suggested_threshold << " suggested threshold)";
9452  else
9453  success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations";
9454  }
9455  else
9456  {
9457  uint64_t current_time = static_cast<uint64_t>(time(NULL));
9459  if (threshold >= pd.m_unlock_time)
9460  success_msg_writer() << "unlocked for " << get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time));
9461  else
9462  success_msg_writer() << "locked for " << get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
9463  }
9464  success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
9465  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9466  return true;
9467  }
9468  }
9469 
9470  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
9471  m_wallet->get_payments_out(payments_out, 0, (uint64_t)-1, m_current_subaddress_account);
9472  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
9473  if (i->first == txid)
9474  {
9475  const tools::wallet2::confirmed_transfer_details &pd = i->second;
9476  uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
9477  uint64_t fee = pd.m_amount_in - pd.m_amount_out;
9478  std::string dests;
9479  for (const auto &d: pd.m_dests) {
9480  if (!dests.empty())
9481  dests += ", ";
9482  dests += get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) + ": " + print_etn(d.amount);
9483  }
9484  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
9485  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9486  payment_id = payment_id.substr(0,16);
9487  success_msg_writer() << "Outgoing transaction found";
9488  success_msg_writer() << "txid: " << txid;
9489  success_msg_writer() << "Height: " << pd.m_block_height;
9491  success_msg_writer() << "Amount: " << print_etn(pd.m_amount_in - change - fee);
9492  success_msg_writer() << "Payment ID: " << payment_id;
9493  success_msg_writer() << "Change: " << print_etn(change);
9494  success_msg_writer() << "Fee: " << print_etn(fee);
9495  success_msg_writer() << "Destinations: " << dests;
9496  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9497  return true;
9498  }
9499  }
9500 
9501  try
9502  {
9503  m_wallet->update_pool_state();
9504  std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
9505  m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account);
9506  for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
9507  const tools::wallet2::payment_details &pd = i->second.m_pd;
9508  if (pd.m_tx_hash == txid)
9509  {
9510  std::string payment_id = string_tools::pod_to_hex(i->first);
9511  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9512  payment_id = payment_id.substr(0,16);
9513  success_msg_writer() << "Unconfirmed incoming transaction found in the txpool";
9514  success_msg_writer() << "txid: " << txid;
9516  success_msg_writer() << "Amount: " << print_etn(pd.m_amount);
9517  success_msg_writer() << "Payment ID: " << payment_id;
9518  success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
9519  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9520  if (i->second.m_double_spend_seen)
9521  success_msg_writer() << tr("Double spend seen on the network: this transaction may or may not end up being mined");
9522  if (i->second.m_nonexistent_utxo_seen)
9523  success_msg_writer() << tr("Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
9524  return true;
9525  }
9526  }
9527  }
9528  catch (...)
9529  {
9530  fail_msg_writer() << "Failed to get pool state";
9531  }
9532 
9533  std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
9534  m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account);
9535  for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
9536  if (i->first == txid)
9537  {
9538  const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
9539  uint64_t amount = pd.m_amount_in;
9540  uint64_t fee = amount - pd.m_amount_out;
9541  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
9542  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9543  payment_id = payment_id.substr(0,16);
9545 
9546  success_msg_writer() << (is_failed ? "Failed" : "Pending") << " outgoing transaction found";
9547  success_msg_writer() << "txid: " << txid;
9549  success_msg_writer() << "Amount: " << print_etn(amount - pd.m_change - fee);
9550  success_msg_writer() << "Payment ID: " << payment_id;
9551  success_msg_writer() << "Change: " << print_etn(pd.m_change);
9552  success_msg_writer() << "Fee: " << print_etn(fee);
9553  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9554  return true;
9555  }
9556  }
9557 
9558  fail_msg_writer() << tr("Transaction ID not found");
9559  return true;
9560 }
9561 //----------------------------------------------------------------------------------------------------
9562 bool simple_wallet::process_command(const std::vector<std::string> &args)
9563 {
9564  return m_cmd_binder.process_command_vec(args);
9565 }
9566 //----------------------------------------------------------------------------------------------------
9568 {
9569  if (m_in_manual_refresh.load(std::memory_order_relaxed))
9570  {
9571  m_wallet->stop();
9572  }
9573  else
9574  {
9575  stop();
9576  }
9577 }
9578 //----------------------------------------------------------------------------------------------------
9579 void simple_wallet::commit_or_save(std::vector<tools::wallet2::pending_tx>& ptx_vector, bool do_not_relay)
9580 {
9581  size_t i = 0;
9582  while (!ptx_vector.empty())
9583  {
9584  auto & ptx = ptx_vector.back();
9585  const crypto::hash txid = get_transaction_hash(ptx.tx);
9586  if (do_not_relay)
9587  {
9588  cryptonote::blobdata blob;
9589  tx_to_blob(ptx.tx, blob);
9591  const std::string filename = "raw_electroneum_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++)));
9592  if (epee::file_io_utils::save_string_to_file(filename, blob_hex))
9593  success_msg_writer(true) << tr("Transaction successfully saved to ") << filename << tr(", txid ") << txid;
9594  else
9595  fail_msg_writer() << tr("Failed to save transaction to ") << filename << tr(", txid ") << txid;
9596  }
9597  else
9598  {
9599  m_wallet->commit_tx(ptx);
9600  success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << txid << ENDL
9601  << tr("You can check its status by using the `show_transfers` command.");
9602  }
9603  // if no exception, remove element from vector
9604  ptx_vector.pop_back();
9605  }
9606 }
9607 //----------------------------------------------------------------------------------------------------
9608 int main(int argc, char* argv[])
9609 {
9610  TRY_ENTRY();
9611 
9612 #ifdef WIN32
9613  // Activate UTF-8 support for Boost filesystem classes on Windows
9614  std::locale::global(boost::locale::generator().generate(""));
9615  boost::filesystem::path::imbue(std::locale());
9616 #endif
9617  // READ IN OPTIONS FROM COMMAND LINE
9618  po::options_description desc_params(wallet_args::tr("Wallet options"));
9619  tools::wallet2::init_options(desc_params);
9620  command_line::add_arg(desc_params, arg_wallet_file);
9621  command_line::add_arg(desc_params, arg_generate_new_wallet);
9622  command_line::add_arg(desc_params, arg_generate_from_device);
9623  command_line::add_arg(desc_params, arg_generate_from_view_key);
9624  command_line::add_arg(desc_params, arg_generate_from_spend_key);
9625  command_line::add_arg(desc_params, arg_generate_from_keys);
9626  command_line::add_arg(desc_params, arg_generate_from_multisig_keys);
9628  command_line::add_arg(desc_params, arg_mnemonic_language);
9629  command_line::add_arg(desc_params, arg_command);
9630 
9631  command_line::add_arg(desc_params, arg_restore_deterministic_wallet );
9632  command_line::add_arg(desc_params, arg_restore_multisig_wallet );
9633  command_line::add_arg(desc_params, arg_non_deterministic );
9634  command_line::add_arg(desc_params, arg_electrum_seed );
9635  command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version);
9636  command_line::add_arg(desc_params, arg_restore_height);
9637  command_line::add_arg(desc_params, arg_restore_date);
9638  command_line::add_arg(desc_params, arg_do_not_relay);
9639  command_line::add_arg(desc_params, arg_create_address_file);
9640  command_line::add_arg(desc_params, arg_subaddress_lookahead);
9641  command_line::add_arg(desc_params, arg_use_english_language_names);
9642  command_line::add_arg(desc_params, arg_long_payment_id_support);
9643  command_line::add_arg(desc_params, arg_account_major_offset);
9646  po::positional_options_description positional_options;
9647  positional_options.add(arg_command.name, -1);
9648 
9649  boost::optional<po::variables_map> vm;
9650  bool should_terminate = false;
9651  std::tie(vm, should_terminate) = wallet_args::main(
9652  argc, argv,
9653  "electroneum-wallet-cli [--wallet-file=<filename>|--generate-new-wallet=<filename>] [<COMMAND>]",
9654  sw::tr("This is the command line electroneum wallet. It needs to connect to a electroneum\ndaemon to work correctly.\nWARNING: Do not reuse your Electroneum keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."),
9655  desc_params,
9656  positional_options,
9657  [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
9658  "electroneum-wallet-cli.log"
9659  );
9660 
9661  if (!vm)
9662  {
9663  return 1;
9664  }
9665 
9666  if (should_terminate)
9667  {
9668  return 0;
9669  }
9670 
9671 
9672  // INITIALISE THE WALLET AND OPEN IT UP (.init)
9674  const bool r = w.init(*vm);
9675  CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet"));
9676 
9677  std::vector<std::string> command = command_line::get_arg(*vm, arg_command);
9678  if (!command.empty())
9679  {
9680  if (!w.process_command(command))
9681  fail_msg_writer() << sw::tr("Unknown command: ") << command.front();
9682  w.stop();
9683  w.deinit();
9684  }
9685  else
9686  {
9687  tools::signal_handler::install([&w](int type) {
9689  {
9690  // must be prompting for password so return and let the signal stop prompt
9691  return;
9692  }
9693 #ifdef WIN32
9694  if (type == CTRL_C_EVENT)
9695 #else
9696  if (type == SIGINT)
9697 #endif
9698  {
9699  // if we're pressing ^C when refreshing, just stop refreshing
9700  w.interrupt();
9701  }
9702  else
9703  {
9704  w.stop();
9705  }
9706  });
9707 
9708  // FINALLY, RUN THE WALLET
9709  w.run();
9710 
9711  // CLOSE WALLET DOWN WHEN CONTROL RETURNS FROM w.run()
9712  w.deinit();
9713  }
9714  return 0;
9715  CATCH_ENTRY_L0("main", 1);
9716 }
9717 
9718 // MMS ---------------------------------------------------------------------------------------------------
9719 
9720 // Access to the message store, or more exactly to the list of the messages that can be changed
9721 // by the idle thread, is guarded by the same mutex-based mechanism as access to the wallet
9722 // as a whole and thus e.g. uses the "LOCK_IDLE_SCOPE" macro. This is a little over-cautious, but
9723 // simple and safe. Care has to be taken however where MMS methods call other simplewallet methods
9724 // that use "LOCK_IDLE_SCOPE" as this cannot be nested!
9725 
9726 // Methods for commands like "export_multisig_info" usually read data from file(s) or write data
9727 // to files. The MMS calls now those methods as well, to produce data for messages and to process data
9728 // from messages. As it would be quite inconvenient for the MMS to write data for such methods to files
9729 // first or get data out of result files after the call, those methods detect a call from the MMS and
9730 // expect data as arguments instead of files and give back data by calling 'process_wallet_created_data'.
9731 
9732 bool simple_wallet::user_confirms(const std::string &question)
9733 {
9734  std::string answer = input_line(question + tr(" (Y/Yes/N/No): "));
9735  return !std::cin.eof() && command_line::is_yes(answer);
9736 }
9737 
9738 bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound)
9739 {
9740  bool valid = false;
9741  try
9742  {
9743  number = boost::lexical_cast<uint32_t>(arg);
9744  valid = (number >= lower_bound) && (number <= upper_bound);
9745  }
9746  catch(const boost::bad_lexical_cast &)
9747  {
9748  }
9749  return valid;
9750 }
9751 
9752 bool simple_wallet::choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice)
9753 {
9754  size_t choices = data_list.size();
9755  if (choices == 1)
9756  {
9757  choice = 0;
9758  return true;
9759  }
9760  mms::message_store& ms = m_wallet->get_message_store();
9761  message_writer() << tr("Choose processing:");
9762  std::string text;
9763  for (size_t i = 0; i < choices; ++i)
9764  {
9765  const mms::processing_data &data = data_list[i];
9766  text = std::to_string(i+1) + ": ";
9767  switch (data.processing)
9768  {
9770  text += tr("Sign tx");
9771  break;
9773  {
9774  mms::message m;
9775  ms.get_message_by_id(data.message_ids[0], m);
9777  {
9778  text += tr("Send the tx for submission to ");
9779  }
9780  else
9781  {
9782  text += tr("Send the tx for signing to ");
9783  }
9785  text += ms.signer_to_string(signer, 50);
9786  break;
9787  }
9789  text += tr("Submit tx");
9790  break;
9791  default:
9792  text += tr("unknown");
9793  break;
9794  }
9795  message_writer() << text;
9796  }
9797 
9798  std::string line = input_line(tr("Choice: "));
9799  if (std::cin.eof() || line.empty())
9800  {
9801  return false;
9802  }
9803  bool choice_ok = get_number_from_arg(line, choice, 1, choices);
9804  if (choice_ok)
9805  {
9806  choice--;
9807  }
9808  else
9809  {
9810  fail_msg_writer() << tr("Wrong choice");
9811  }
9812  return choice_ok;
9813 }
9814 
9815 void simple_wallet::list_mms_messages(const std::vector<mms::message> &messages)
9816 {
9817  message_writer() << boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") % tr("Id") % tr("I/O") % tr("Authorized Signer")
9818  % tr("Message Type") % tr("Height") % tr("R") % tr("Message State") % tr("Since");
9819  mms::message_store& ms = m_wallet->get_message_store();
9820  uint64_t now = (uint64_t)time(NULL);
9821  for (size_t i = 0; i < messages.size(); ++i)
9822  {
9823  const mms::message &m = messages[i];
9824  const mms::authorized_signer &signer = ms.get_signer(m.signer_index);
9827  boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
9828  m.id %
9830  ms.signer_to_string(signer, 30) %
9832  m.wallet_height %
9833  m.round %
9835  (tools::get_human_readable_timestamp(m.modified) + ", " + get_human_readable_timespan(std::chrono::seconds(now - m.modified)) + tr(" ago"));
9836  }
9837 }
9838 
9839 void simple_wallet::list_signers(const std::vector<mms::authorized_signer> &signers)
9840 {
9841  message_writer() << boost::format("%2s %-20s %-s") % tr("#") % tr("Label") % tr("Transport Address");
9842  message_writer() << boost::format("%2s %-20s %-s") % "" % tr("Auto-Config Token") % tr("Electroneum Address");
9843  for (size_t i = 0; i < signers.size(); ++i)
9844  {
9845  const mms::authorized_signer &signer = signers[i];
9846  std::string label = signer.label.empty() ? tr("<not set>") : signer.label;
9847  std::string electroneum_address;
9848  if (signer.etn_address_known)
9849  {
9850  electroneum_address = get_account_address_as_str(m_wallet->nettype(), false, signer.etn_address);
9851  }
9852  else
9853  {
9854  electroneum_address = tr("<not set>");
9855  }
9856  std::string transport_address = signer.transport_address.empty() ? tr("<not set>") : signer.transport_address;
9857  message_writer() << boost::format("%2s %-20s %-s") % (i + 1) % label % transport_address;
9858  message_writer() << boost::format("%2s %-20s %-s") % "" % signer.auto_config_token % electroneum_address;
9859  message_writer() << "";
9860  }
9861 }
9862 
9863 void simple_wallet::add_signer_config_messages()
9864 {
9865  mms::message_store& ms = m_wallet->get_message_store();
9867  ms.get_signer_config(signer_config);
9868 
9869  const std::vector<mms::authorized_signer> signers = ms.get_all_signers();
9870  mms::multisig_wallet_state state = get_multisig_wallet_state();
9871  uint32_t num_authorized_signers = ms.get_num_authorized_signers();
9872  for (uint32_t i = 1 /* without me */; i < num_authorized_signers; ++i)
9873  {
9875  }
9876 }
9877 
9878 void simple_wallet::show_message(const mms::message &m)
9879 {
9880  mms::message_store& ms = m_wallet->get_message_store();
9881  const mms::authorized_signer &signer = ms.get_signer(m.signer_index);
9882  bool display_content;
9883  std::string sanitized_text;
9884  switch (m.type)
9885  {
9889  display_content = true;
9890  ms.get_sanitized_message_text(m, sanitized_text);
9891  break;
9892  default:
9893  display_content = false;
9894  }
9895  uint64_t now = (uint64_t)time(NULL);
9896  message_writer() << "";
9897  message_writer() << tr("Message ") << m.id;
9898  message_writer() << tr("In/out: ") << ms.message_direction_to_string(m.direction);
9899  message_writer() << tr("Type: ") << ms.message_type_to_string(m.type);
9900  message_writer() << tr("State: ") << boost::format(tr("%s since %s, %s ago")) %
9901  ms.message_state_to_string(m.state) % tools::get_human_readable_timestamp(m.modified) % get_human_readable_timespan(std::chrono::seconds(now - m.modified));
9902  if (m.sent == 0)
9903  {
9904  message_writer() << tr("Sent: Never");
9905  }
9906  else
9907  {
9908  message_writer() << boost::format(tr("Sent: %s, %s ago")) %
9909  tools::get_human_readable_timestamp(m.sent) % get_human_readable_timespan(std::chrono::seconds(now - m.sent));
9910  }
9911  message_writer() << tr("Authorized signer: ") << ms.signer_to_string(signer, 100);
9912  message_writer() << tr("Content size: ") << m.content.length() << tr(" bytes");
9913  message_writer() << tr("Content: ") << (display_content ? sanitized_text : tr("(binary data)"));
9914 
9915  if (m.type == mms::message_type::note)
9916  {
9917  // Showing a note and read its text is "processing" it: Set the state accordingly
9918  // which will also delete it from Bitmessage as a side effect
9919  // (Without this little "twist" it would never change the state, and never get deleted)
9921  }
9922 }
9923 
9924 void simple_wallet::ask_send_all_ready_messages()
9925 {
9926  mms::message_store& ms = m_wallet->get_message_store();
9927  std::vector<mms::message> ready_messages;
9928  const std::vector<mms::message> &messages = ms.get_all_messages();
9929  for (size_t i = 0; i < messages.size(); ++i)
9930  {
9931  const mms::message &m = messages[i];
9933  {
9934  ready_messages.push_back(m);
9935  }
9936  }
9937  if (ready_messages.size() != 0)
9938  {
9939  list_mms_messages(ready_messages);
9940  bool send = ms.get_auto_send();
9941  if (!send)
9942  {
9943  send = user_confirms(tr("Send these messages now?"));
9944  }
9945  if (send)
9946  {
9947  mms::multisig_wallet_state state = get_multisig_wallet_state();
9948  for (size_t i = 0; i < ready_messages.size(); ++i)
9949  {
9950  ms.send_message(state, ready_messages[i].id);
9951  ms.set_message_processed_or_sent(ready_messages[i].id);
9952  }
9953  success_msg_writer() << tr("Queued for sending.");
9954  }
9955  }
9956 }
9957 
9958 bool simple_wallet::get_message_from_arg(const std::string &arg, mms::message &m)
9959 {
9960  mms::message_store& ms = m_wallet->get_message_store();
9961  bool valid_id = false;
9962  uint32_t id;
9963  try
9964  {
9965  id = (uint32_t)boost::lexical_cast<uint32_t>(arg);
9966  valid_id = ms.get_message_by_id(id, m);
9967  }
9968  catch (const boost::bad_lexical_cast &)
9969  {
9970  }
9971  if (!valid_id)
9972  {
9973  fail_msg_writer() << tr("Invalid message id");
9974  }
9975  return valid_id;
9976 }
9977 
9978 void simple_wallet::mms_init(const std::vector<std::string> &args)
9979 {
9980  if (args.size() != 3)
9981  {
9982  fail_msg_writer() << tr("usage: mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
9983  return;
9984  }
9985  mms::message_store& ms = m_wallet->get_message_store();
9986  if (ms.get_active())
9987  {
9988  if (!user_confirms(tr("The MMS is already initialized. Re-initialize by deleting all signer info and messages?")))
9989  {
9990  return;
9991  }
9992  }
9993  uint32_t num_required_signers;
9994  uint32_t num_authorized_signers;
9995  const std::string &mn = args[0];
9996  std::vector<std::string> numbers;
9997  boost::split(numbers, mn, boost::is_any_of("/"));
9998  bool mn_ok = (numbers.size() == 2)
9999  && get_number_from_arg(numbers[1], num_authorized_signers, 2, 100)
10000  && get_number_from_arg(numbers[0], num_required_signers, 2, num_authorized_signers);
10001  if (!mn_ok)
10002  {
10003  fail_msg_writer() << tr("Error in the number of required signers and/or authorized signers");
10004  return;
10005  }
10006  LOCK_IDLE_SCOPE();
10007  ms.init(get_multisig_wallet_state(), args[1], args[2], num_authorized_signers, num_required_signers);
10008 }
10009 
10010 void simple_wallet::mms_info(const std::vector<std::string> &args)
10011 {
10012  mms::message_store& ms = m_wallet->get_message_store();
10013  if (ms.get_active())
10014  {
10015  message_writer() << boost::format("The MMS is active for %s/%s multisig.")
10017  }
10018  else
10019  {
10020  message_writer() << tr("The MMS is not active.");
10021  }
10022 }
10023 
10024 void simple_wallet::mms_signer(const std::vector<std::string> &args)
10025 {
10026  mms::message_store& ms = m_wallet->get_message_store();
10027  const std::vector<mms::authorized_signer> &signers = ms.get_all_signers();
10028  if (args.size() == 0)
10029  {
10030  // Without further parameters list all defined signers
10031  list_signers(signers);
10032  return;
10033  }
10034 
10035  uint32_t index;
10036  bool index_valid = get_number_from_arg(args[0], index, 1, ms.get_num_authorized_signers());
10037  if (index_valid)
10038  {
10039  index--;
10040  }
10041  else
10042  {
10043  fail_msg_writer() << tr("Invalid signer number ") + args[0];
10044  return;
10045  }
10046  if ((args.size() < 2) || (args.size() > 4))
10047  {
10048  fail_msg_writer() << tr("mms signer [<number> <label> [<transport_address> [<electroneum_address>]]]");
10049  return;
10050  }
10051 
10052  boost::optional<string> label = args[1];
10053  boost::optional<string> transport_address;
10054  if (args.size() >= 3)
10055  {
10056  transport_address = args[2];
10057  }
10058  boost::optional<cryptonote::account_public_address> electroneum_address;
10059  LOCK_IDLE_SCOPE();
10060  mms::multisig_wallet_state state = get_multisig_wallet_state();
10061  if (args.size() == 4)
10062  {
10064  bool ok = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[3], oa_prompter);
10065  if (!ok)
10066  {
10067  fail_msg_writer() << tr("Invalid Electroneum address");
10068  return;
10069  }
10070  electroneum_address = info.address;
10071  const std::vector<mms::message> &messages = ms.get_all_messages();
10072  if ((messages.size() > 0) || state.multisig)
10073  {
10074  fail_msg_writer() << tr("Wallet state does not allow changing Electroneum addresses anymore");
10075  return;
10076  }
10077  }
10078  ms.set_signer(state, index, label, transport_address, electroneum_address);
10079 }
10080 
10081 void simple_wallet::mms_list(const std::vector<std::string> &args)
10082 {
10083  mms::message_store& ms = m_wallet->get_message_store();
10084  if (args.size() != 0)
10085  {
10086  fail_msg_writer() << tr("Usage: mms list");
10087  return;
10088  }
10089  LOCK_IDLE_SCOPE();
10090  const std::vector<mms::message> &messages = ms.get_all_messages();
10091  list_mms_messages(messages);
10092 }
10093 
10094 void simple_wallet::mms_next(const std::vector<std::string> &args)
10095 {
10096  mms::message_store& ms = m_wallet->get_message_store();
10097  if ((args.size() > 1) || ((args.size() == 1) && (args[0] != "sync")))
10098  {
10099  fail_msg_writer() << tr("Usage: mms next [sync]");
10100  return;
10101  }
10102  bool avail = false;
10103  std::vector<mms::processing_data> data_list;
10104  bool force_sync = false;
10105  uint32_t choice = 0;
10106  {
10107  LOCK_IDLE_SCOPE();
10108  if ((args.size() == 1) && (args[0] == "sync"))
10109  {
10110  // Force the MMS to process any waiting sync info although on its own it would just ignore
10111  // those messages because no need to process them can be seen
10112  force_sync = true;
10113  }
10114  string wait_reason;
10115  {
10116  avail = ms.get_processable_messages(get_multisig_wallet_state(), force_sync, data_list, wait_reason);
10117  }
10118  if (avail)
10119  {
10120  avail = choose_mms_processing(data_list, choice);
10121  }
10122  else if (!wait_reason.empty())
10123  {
10124  message_writer() << tr("No next step: ") << wait_reason;
10125  }
10126  }
10127  if (avail)
10128  {
10129  mms::processing_data data = data_list[choice];
10130  bool command_successful = false;
10131  switch(data.processing)
10132  {
10134  message_writer() << tr("prepare_multisig");
10135  command_successful = prepare_multisig_main(std::vector<std::string>(), true);
10136  break;
10137 
10139  {
10140  message_writer() << tr("make_multisig");
10141  size_t number_of_key_sets = data.message_ids.size();
10142  std::vector<std::string> sig_args(number_of_key_sets + 1);
10143  sig_args[0] = std::to_string(ms.get_num_required_signers());
10144  for (size_t i = 0; i < number_of_key_sets; ++i)
10145  {
10146  mms::message m = ms.get_message_by_id(data.message_ids[i]);
10147  sig_args[i+1] = m.content;
10148  }
10149  command_successful = make_multisig_main(sig_args, true);
10150  break;
10151  }
10152 
10154  {
10155  message_writer() << tr("exchange_multisig_keys");
10156  size_t number_of_key_sets = data.message_ids.size();
10157  // Other than "make_multisig" only the key sets as parameters, no num_required_signers
10158  std::vector<std::string> sig_args(number_of_key_sets);
10159  for (size_t i = 0; i < number_of_key_sets; ++i)
10160  {
10161  mms::message m = ms.get_message_by_id(data.message_ids[i]);
10162  sig_args[i] = m.content;
10163  }
10164  command_successful = exchange_multisig_keys_main(sig_args, true);
10165  break;
10166  }
10167 
10169  {
10170  message_writer() << tr("export_multisig_info");
10171  std::vector<std::string> export_args;
10172  export_args.push_back("MMS"); // dummy filename
10173  command_successful = export_multisig_main(export_args, true);
10174  break;
10175  }
10176 
10178  {
10179  message_writer() << tr("import_multisig_info");
10180  std::vector<std::string> import_args;
10181  for (size_t i = 0; i < data.message_ids.size(); ++i)
10182  {
10183  mms::message m = ms.get_message_by_id(data.message_ids[i]);
10184  import_args.push_back(m.content);
10185  }
10186  command_successful = import_multisig_main(import_args, true);
10187  break;
10188  }
10189 
10191  {
10192  message_writer() << tr("sign_multisig");
10193  std::vector<std::string> sign_args;
10194  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10195  sign_args.push_back(m.content);
10196  command_successful = sign_multisig_main(sign_args, true);
10197  break;
10198  }
10199 
10201  {
10202  message_writer() << tr("submit_multisig");
10203  std::vector<std::string> submit_args;
10204  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10205  submit_args.push_back(m.content);
10206  command_successful = submit_multisig_main(submit_args, true);
10207  break;
10208  }
10209 
10211  {
10212  message_writer() << tr("Send tx");
10213  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10214  LOCK_IDLE_SCOPE();
10215  ms.add_message(get_multisig_wallet_state(), data.receiving_signer_index, m.type, mms::message_direction::out,
10216  m.content);
10217  command_successful = true;
10218  break;
10219  }
10220 
10222  {
10223  message_writer() << tr("Process signer config");
10224  LOCK_IDLE_SCOPE();
10225  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10227  mms::multisig_wallet_state state = get_multisig_wallet_state();
10228  if (!me.auto_config_running)
10229  {
10230  // If no auto-config is running, the config sent may be unsolicited or problematic
10231  // so show what arrived and ask for confirmation before taking it in
10232  std::vector<mms::authorized_signer> signers;
10233  ms.unpack_signer_config(state, m.content, signers);
10234  list_signers(signers);
10235  if (!user_confirms(tr("Replace current signer config with the one displayed above?")))
10236  {
10237  break;
10238  }
10239  }
10241  ms.stop_auto_config();
10242  list_signers(ms.get_all_signers());
10243  command_successful = true;
10244  break;
10245  }
10246 
10248  {
10249  message_writer() << tr("Process auto config data");
10250  LOCK_IDLE_SCOPE();
10251  for (size_t i = 0; i < data.message_ids.size(); ++i)
10252  {
10254  }
10255  ms.stop_auto_config();
10256  list_signers(ms.get_all_signers());
10257  add_signer_config_messages();
10258  command_successful = true;
10259  break;
10260  }
10261 
10262  default:
10263  message_writer() << tr("Nothing ready to process");
10264  break;
10265  }
10266 
10267  if (command_successful)
10268  {
10269  {
10270  LOCK_IDLE_SCOPE();
10271  ms.set_messages_processed(data);
10272  ask_send_all_ready_messages();
10273  }
10274  }
10275  }
10276 }
10277 
10278 void simple_wallet::mms_sync(const std::vector<std::string> &args)
10279 {
10280  mms::message_store& ms = m_wallet->get_message_store();
10281  if (args.size() != 0)
10282  {
10283  fail_msg_writer() << tr("Usage: mms sync");
10284  return;
10285  }
10286  // Force the start of a new sync round, for exceptional cases where something went wrong
10287  // Can e.g. solve the problem "This signature was made with stale data" after trying to
10288  // create 2 transactions in a row somehow
10289  // Code is identical to the code for 'message_processing::create_sync_data'
10290  message_writer() << tr("export_multisig_info");
10291  std::vector<std::string> export_args;
10292  export_args.push_back("MMS"); // dummy filename
10293  export_multisig_main(export_args, true);
10294  ask_send_all_ready_messages();
10295 }
10296 
10297 void simple_wallet::mms_transfer(const std::vector<std::string> &args)
10298 {
10299  // It's too complicated to check any arguments here, just let 'transfer_main' do the whole job
10300  transfer_main(Transfer, args, true);
10301 }
10302 
10303 void simple_wallet::mms_delete(const std::vector<std::string> &args)
10304 {
10305  if (args.size() != 1)
10306  {
10307  fail_msg_writer() << tr("Usage: mms delete (<message_id> | all)");
10308  return;
10309  }
10310  LOCK_IDLE_SCOPE();
10311  mms::message_store& ms = m_wallet->get_message_store();
10312  if (args[0] == "all")
10313  {
10314  if (user_confirms(tr("Delete all messages?")))
10315  {
10316  ms.delete_all_messages();
10317  }
10318  }
10319  else
10320  {
10321  mms::message m;
10322  bool valid_id = get_message_from_arg(args[0], m);
10323  if (valid_id)
10324  {
10325  // If only a single message and not all delete even if unsent / unprocessed
10326  ms.delete_message(m.id);
10327  }
10328  }
10329 }
10330 
10331 void simple_wallet::mms_send(const std::vector<std::string> &args)
10332 {
10333  if (args.size() == 0)
10334  {
10335  ask_send_all_ready_messages();
10336  return;
10337  }
10338  else if (args.size() != 1)
10339  {
10340  fail_msg_writer() << tr("Usage: mms send [<message_id>]");
10341  return;
10342  }
10343  LOCK_IDLE_SCOPE();
10344  mms::message_store& ms = m_wallet->get_message_store();
10345  mms::message m;
10346  bool valid_id = get_message_from_arg(args[0], m);
10347  if (valid_id)
10348  {
10349  ms.send_message(get_multisig_wallet_state(), m.id);
10350  }
10351 }
10352 
10353 void simple_wallet::mms_receive(const std::vector<std::string> &args)
10354 {
10355  if (args.size() != 0)
10356  {
10357  fail_msg_writer() << tr("Usage: mms receive");
10358  return;
10359  }
10360  std::vector<mms::message> new_messages;
10361  LOCK_IDLE_SCOPE();
10362  mms::message_store& ms = m_wallet->get_message_store();
10363  bool avail = ms.check_for_messages(get_multisig_wallet_state(), new_messages);
10364  if (avail)
10365  {
10366  list_mms_messages(new_messages);
10367  }
10368 }
10369 
10370 void simple_wallet::mms_export(const std::vector<std::string> &args)
10371 {
10372  if (args.size() != 1)
10373  {
10374  fail_msg_writer() << tr("Usage: mms export <message_id>");
10375  return;
10376  }
10377  LOCK_IDLE_SCOPE();
10378  mms::message_store& ms = m_wallet->get_message_store();
10379  mms::message m;
10380  bool valid_id = get_message_from_arg(args[0], m);
10381  if (valid_id)
10382  {
10383  const std::string filename = "mms_message_content";
10385  {
10386  success_msg_writer() << tr("Message content saved to: ") << filename;
10387  }
10388  else
10389  {
10390  fail_msg_writer() << tr("Failed to to save message content");
10391  }
10392  }
10393 }
10394 
10395 void simple_wallet::mms_note(const std::vector<std::string> &args)
10396 {
10397  mms::message_store& ms = m_wallet->get_message_store();
10398  if (args.size() == 0)
10399  {
10400  LOCK_IDLE_SCOPE();
10401  const std::vector<mms::message> &messages = ms.get_all_messages();
10402  for (size_t i = 0; i < messages.size(); ++i)
10403  {
10404  const mms::message &m = messages[i];
10406  {
10407  show_message(m);
10408  }
10409  }
10410  return;
10411  }
10412  if (args.size() < 2)
10413  {
10414  fail_msg_writer() << tr("Usage: mms note [<label> <text>]");
10415  return;
10416  }
10417  uint32_t signer_index;
10418  bool found = ms.get_signer_index_by_label(args[0], signer_index);
10419  if (!found)
10420  {
10421  fail_msg_writer() << tr("No signer found with label ") << args[0];
10422  return;
10423  }
10424  std::string note = "";
10425  for (size_t n = 1; n < args.size(); ++n)
10426  {
10427  if (n > 1)
10428  {
10429  note += " ";
10430  }
10431  note += args[n];
10432  }
10433  LOCK_IDLE_SCOPE();
10434  ms.add_message(get_multisig_wallet_state(), signer_index, mms::message_type::note,
10436  ask_send_all_ready_messages();
10437 }
10438 
10439 void simple_wallet::mms_show(const std::vector<std::string> &args)
10440 {
10441  if (args.size() != 1)
10442  {
10443  fail_msg_writer() << tr("Usage: mms show <message_id>");
10444  return;
10445  }
10446  LOCK_IDLE_SCOPE();
10447  mms::message_store& ms = m_wallet->get_message_store();
10448  mms::message m;
10449  bool valid_id = get_message_from_arg(args[0], m);
10450  if (valid_id)
10451  {
10452  show_message(m);
10453  }
10454 }
10455 
10456 void simple_wallet::mms_set(const std::vector<std::string> &args)
10457 {
10458  bool set = args.size() == 2;
10459  bool query = args.size() == 1;
10460  if (!set && !query)
10461  {
10462  fail_msg_writer() << tr("Usage: mms set <option_name> [<option_value>]");
10463  return;
10464  }
10465  mms::message_store& ms = m_wallet->get_message_store();
10466  LOCK_IDLE_SCOPE();
10467  if (args[0] == "auto-send")
10468  {
10469  if (set)
10470  {
10471  bool result;
10472  bool ok = parse_bool(args[1], result);
10473  if (ok)
10474  {
10475  ms.set_auto_send(result);
10476  }
10477  else
10478  {
10479  fail_msg_writer() << tr("Wrong option value");
10480  }
10481  }
10482  else
10483  {
10484  message_writer() << (ms.get_auto_send() ? tr("Auto-send is on") : tr("Auto-send is off"));
10485  }
10486  }
10487  else
10488  {
10489  fail_msg_writer() << tr("Unknown option");
10490  }
10491 }
10492 
10493 void simple_wallet::mms_help(const std::vector<std::string> &args)
10494 {
10495  if (args.size() > 1)
10496  {
10497  fail_msg_writer() << tr("Usage: mms help [<subcommand>]");
10498  return;
10499  }
10500  std::vector<std::string> help_args;
10501  help_args.push_back("mms");
10502  if (args.size() == 1)
10503  {
10504  help_args.push_back(args[0]);
10505  }
10506  help(help_args);
10507 }
10508 
10509 void simple_wallet::mms_send_signer_config(const std::vector<std::string> &args)
10510 {
10511  if (args.size() != 0)
10512  {
10513  fail_msg_writer() << tr("Usage: mms send_signer_config");
10514  return;
10515  }
10516  mms::message_store& ms = m_wallet->get_message_store();
10517  if (!ms.signer_config_complete())
10518  {
10519  fail_msg_writer() << tr("Signer config not yet complete");
10520  return;
10521  }
10522  LOCK_IDLE_SCOPE();
10523  add_signer_config_messages();
10524  ask_send_all_ready_messages();
10525 }
10526 
10527 void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args)
10528 {
10529  mms::message_store& ms = m_wallet->get_message_store();
10530  uint32_t other_signers = ms.get_num_authorized_signers() - 1;
10531  size_t args_size = args.size();
10532  if ((args_size != 0) && (args_size != other_signers))
10533  {
10534  fail_msg_writer() << tr("Usage: mms start_auto_config [<label> <label> ...]");
10535  return;
10536  }
10537  if ((args_size == 0) && !ms.signer_labels_complete())
10538  {
10539  fail_msg_writer() << tr("There are signers without a label set. Complete labels before auto-config or specify them as parameters here.");
10540  return;
10541  }
10543  if (me.auto_config_running)
10544  {
10545  if (!user_confirms(tr("Auto-config is already running. Cancel and restart?")))
10546  {
10547  return;
10548  }
10549  }
10550  LOCK_IDLE_SCOPE();
10551  mms::multisig_wallet_state state = get_multisig_wallet_state();
10552  if (args_size != 0)
10553  {
10554  // Set (or overwrite) all the labels except "me" from the arguments
10555  for (uint32_t i = 1; i < (other_signers + 1); ++i)
10556  {
10557  ms.set_signer(state, i, args[i - 1], boost::none, boost::none);
10558  }
10559  }
10561  // List the signers to show the generated auto-config tokens
10562  list_signers(ms.get_all_signers());
10563 }
10564 
10565 void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args)
10566 {
10567  if (args.size() != 0)
10568  {
10569  fail_msg_writer() << tr("Usage: mms stop_auto_config");
10570  return;
10571  }
10572  if (!user_confirms(tr("Delete any auto-config tokens and stop auto-config?")))
10573  {
10574  return;
10575  }
10576  mms::message_store& ms = m_wallet->get_message_store();
10577  LOCK_IDLE_SCOPE();
10578  ms.stop_auto_config();
10579 }
10580 
10581 void simple_wallet::mms_auto_config(const std::vector<std::string> &args)
10582 {
10583  if (args.size() != 1)
10584  {
10585  fail_msg_writer() << tr("Usage: mms auto_config <auto_config_token>");
10586  return;
10587  }
10588  mms::message_store& ms = m_wallet->get_message_store();
10589  std::string adjusted_token;
10590  if (!ms.check_auto_config_token(args[0], adjusted_token))
10591  {
10592  fail_msg_writer() << tr("Invalid auto-config token");
10593  return;
10594  }
10596  if (me.auto_config_running)
10597  {
10598  if (!user_confirms(tr("Auto-config already running. Cancel and restart?")))
10599  {
10600  return;
10601  }
10602  }
10603  LOCK_IDLE_SCOPE();
10604  ms.add_auto_config_data_message(get_multisig_wallet_state(), adjusted_token);
10605  ask_send_all_ready_messages();
10606 }
10607 
10608 bool simple_wallet::mms(const std::vector<std::string> &args)
10609 {
10610  try
10611  {
10612  m_wallet->get_multisig_wallet_state();
10613  }
10614  catch(const std::exception &e)
10615  {
10616  fail_msg_writer() << tr("MMS not available in this wallet");
10617  return true;
10618  }
10619 
10620  try
10621  {
10622  mms::message_store& ms = m_wallet->get_message_store();
10623  if (args.size() == 0)
10624  {
10625  mms_info(args);
10626  return true;
10627  }
10628 
10629  const std::string &sub_command = args[0];
10630  std::vector<std::string> mms_args = args;
10631  mms_args.erase(mms_args.begin());
10632 
10633  if (sub_command == "init")
10634  {
10635  mms_init(mms_args);
10636  return true;
10637  }
10638  if (!ms.get_active())
10639  {
10640  fail_msg_writer() << tr("The MMS is not active. Activate using the \"mms init\" command");
10641  return true;
10642  }
10643  else if (sub_command == "info")
10644  {
10645  mms_info(mms_args);
10646  }
10647  else if (sub_command == "signer")
10648  {
10649  mms_signer(mms_args);
10650  }
10651  else if (sub_command == "list")
10652  {
10653  mms_list(mms_args);
10654  }
10655  else if (sub_command == "next")
10656  {
10657  mms_next(mms_args);
10658  }
10659  else if (sub_command == "sync")
10660  {
10661  mms_sync(mms_args);
10662  }
10663  else if (sub_command == "transfer")
10664  {
10665  mms_transfer(mms_args);
10666  }
10667  else if (sub_command == "delete")
10668  {
10669  mms_delete(mms_args);
10670  }
10671  else if (sub_command == "send")
10672  {
10673  mms_send(mms_args);
10674  }
10675  else if (sub_command == "receive")
10676  {
10677  mms_receive(mms_args);
10678  }
10679  else if (sub_command == "export")
10680  {
10681  mms_export(mms_args);
10682  }
10683  else if (sub_command == "note")
10684  {
10685  mms_note(mms_args);
10686  }
10687  else if (sub_command == "show")
10688  {
10689  mms_show(mms_args);
10690  }
10691  else if (sub_command == "set")
10692  {
10693  mms_set(mms_args);
10694  }
10695  else if (sub_command == "help")
10696  {
10697  mms_help(mms_args);
10698  }
10699  else if (sub_command == "send_signer_config")
10700  {
10701  mms_send_signer_config(mms_args);
10702  }
10703  else if (sub_command == "start_auto_config")
10704  {
10705  mms_start_auto_config(mms_args);
10706  }
10707  else if (sub_command == "stop_auto_config")
10708  {
10709  mms_stop_auto_config(mms_args);
10710  }
10711  else if (sub_command == "auto_config")
10712  {
10713  mms_auto_config(mms_args);
10714  }
10715  else
10716  {
10717  fail_msg_writer() << tr("Invalid MMS subcommand");
10718  }
10719  }
10720  catch (const tools::error::no_connection_to_daemon &e)
10721  {
10722  fail_msg_writer() << tr("Error in MMS command: ") << e.what() << " " << e.request();
10723  }
10724  catch (const std::exception &e)
10725  {
10726  fail_msg_writer() << tr("Error in MMS command: ") << e.what();
10727  PRINT_USAGE(USAGE_MMS);
10728  return true;
10729  }
10730  return true;
10731 }
10732 // End MMS ------------------------------------------------------------------------------------------------
10733 
const char * res
Definition: hmac_keccak.cpp:41
static bool parse_long_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition: wallet2.cpp:5712
const char *const ELECTRONEUM_RELEASE_NAME
#define tr(x)
Definition: common_defines.h:4
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::vector< crypto::key_image > key_images
Definition: wallet2.h:506
#define MERROR(x)
Definition: misc_log_ex.h:73
const config_t & get_config(network_type nettype)
uint64_t modified
static const char * tr(const char *str)
Definition: simplewallet.h:70
bool is_no(const std::string &str)
const std::vector< message > & get_all_messages() const
const CharType(& source)[N]
Definition: pointer.h:1147
boost::optional< wipeable_string > parse_hexstr() const
static std::atomic< bool > is_prompting
Definition: password.h:55
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
message_type type
command_line::arg_descriptor< std::string > arg_wallet_file()
Definition: wallet_args.cpp:76
size_t size() const noexcept
std::vector< const Language::Base * > get_language_list()
const std::string & request() const
#define MINFO(x)
Definition: misc_log_ex.h:75
#define CHECK_SIMPLE_VARIABLE(name, f, help)
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1188
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:362
void save(Archive &a, const std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
Information representing errors in application but application will keep running. ...
#define LOG_PRINT_L1(x)
Definition: misc_log_ex.h:100
static void wallet_exists(const std::string &file_path, bool &keys_file_exists, bool &wallet_file_exists)
Check if wallet keys and bin files exist.
Definition: wallet2.cpp:5697
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:318
choice
Definition: lmdb.cpp:40
message_type
Definition: message_store.h:55
void set_messages_processed(const processing_data &data)
enum tools::wallet2::unconfirmed_transfer_details::@63 m_state
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
const command_line::arg_descriptor< std::vector< std::string > > arg_command
::std::string string
Definition: gtest-port.h:1097
const std::vector< authorized_signer > & get_all_signers() const
#define HF_VERSION_ENFORCE_0_DECOY_TXS
static const char * message_direction_to_string(message_direction direction)
#define MIN_RING_SIZE
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
bool is_local_address(const std::string &address)
Definition: util.cpp:874
std::vector< std::pair< uint64_t, crypto::hash > > m_uses
Definition: wallet2.h:322
std::string print_etn(uint64_t amount, unsigned int decimal_point)
std::string join_priority_strings(const char *delimiter)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
#define CRYPTONOTE_MAX_BLOCK_NUMBER
bool check_for_messages(const multisig_wallet_state &state, std::vector< message > &messages)
uint32_t receiving_signer_index
uint32_t signer_index
bool is_yes(const std::string &str)
const std::string old_language_name
void mlog_set_log(const char *log)
Definition: mlog.cpp:288
void set_handler(const std::string &cmd, const callback &hndlr, const std::string &usage="", const std::string &description="")
uint64_t height
Definition: blockchain.cpp:91
#define CORE_RPC_STATUS_BUSY
void unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, std::vector< authorized_signer > &signers)
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
std::vector< pending_tx > ptx
Definition: wallet2.h:505
const char * key
Definition: hmac_keccak.cpp:39
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_json(const boost::program_options::variables_map &vm, bool unattended, const std::string &json_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 if no errors.
Definition: wallet2.cpp:1227
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:423
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
STL namespace.
unsigned short uint16_t
Definition: stdint.h:125
SECP256K1_API int secp256k1_ec_pubkey_serialize(const secp256k1_context *ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey *pubkey, unsigned int flags) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
std::vector< uint64_t > key_offsets
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:400
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
bool parse_priority(const std::string &arg, uint32_t &priority)
const char * tr(const char *str)
Definition: wallet_args.cpp:81
bool check_auto_config_token(const std::string &raw_token, std::string &adjusted_token) const
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
std::string get_unit(unsigned int decimal_point)
bool is_file_exist(const std::string &path)
Definition: file_io_utils.h:66
#define HF_VERSION_ENFORCE_0_DECOY_TXS_END
bool process_command(const std::vector< std::string > &args)
unsigned char uint8_t
Definition: stdint.h:124
#define SECP256K1_CONTEXT_SIGN
Definition: secp256k1.h:207
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
std::string & trim(std::string &str)
Definition: string_tools.h:288
const char * name
std::unordered_set< crypto::public_key > m_signers
Definition: wallet2.h:513
#define SCOPED_WALLET_UNLOCK()
static std::string device_name_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1183
#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code)
bool get_account_address_from_str_or_url(address_parse_info &info, network_type nettype, const std::string &str_or_url, std::function< std::string(const std::string &, const std::vector< std::string > &, bool)> dns_confirm)
std::vector< uint8_t > extra
BackgroundMiningSetupType
Definition: wallet2.h:231
bool hex_to_pod(T &pod) const
#define CORE_RPC_STATUS_OK
for(i=1;i< 1;++i) fe_sq(t0
static boost::optional< password_container > prompt(bool verify, const char *mesage="Password", bool hide_input=true)
Definition: password.cpp:253
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:385
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:422
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
TransferType
#define SECP256K1_EC_UNCOMPRESSED
Definition: secp256k1.h:214
uint32_t wallet_height
std::vector< tx_construction_data > txes
Definition: wallet2.h:499
void send_message(const multisig_wallet_state &state, uint32_t id)
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
struct secp256k1_context_struct secp256k1_context
Definition: secp256k1.h:50
void process_auto_config_data_message(uint32_t id)
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
Manages wallet operations. This is the most abstracted wallet class.
Definition: simplewallet.h:67
return true
const char *const ELECTRONEUM_VERSION_FULL
std::string transport_address
blobdata tx_to_blob(const transaction &tx)
mdb_size_t count(MDB_cursor *cur)
time_t time
Definition: blockchain.cpp:93
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
std::string to_string() const
void split(std::vector< wipeable_string > &fields) const
unsigned int get_default_decimal_point()
cryptonote::account_public_address etn_address
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(const secp256k1_context *ctx, const unsigned char *seckey) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2)
std::vector< transfer_details > transfer_container
Definition: wallet2.h:449
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
#define MAKE_CORE_RPC_VERSION(major, minor)
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
unsigned int uint32_t
Definition: stdint.h:126
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
cryptonote::simple_wallet sw
std::pair< size_t, wallet2::transfer_container > transfers
Definition: wallet2.h:500
std::set< uint32_t > subaddr_indices
Definition: wallet2.h:432
std::string mlog_get_categories()
Definition: mlog.cpp:276
size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token)
void sc_reduce32(unsigned char *)
message_processing processing
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
std::string auto_config_token
bool empty() const noexcept
bool signer_labels_complete() const
void delete_message(uint32_t id)
const authorized_signer & get_signer(uint32_t index) const
unsigned __int64 uint64_t
Definition: stdint.h:136
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:424
bool init(const boost::program_options::variables_map &vm)
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
command_line::arg_descriptor< std::string > arg_generate_from_json()
Definition: wallet_args.cpp:72
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
cryptonote::transaction_prefix m_tx
Definition: wallet2.h:304
uint64_t amount
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
const scanty_outs_t & scanty_outs() const
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
Definition: file_io_utils.h:73
#define false
Definition: stdbool.h:38
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const
void get_signer_config(std::string &signer_config)
Header file that declares simple_wallet class.
static void init_options(boost::program_options::options_description &desc_params)
Definition: wallet2.cpp:1193
const base::type::char_t * unit
static const char * message_type_to_string(message_type type)
#define LOCK_IDLE_SCOPE()
bool is_subaddress
std::vector< uint8_t > extra
Definition: wallet2.h:426
bool is_integrated
uint32_t get_num_required_signers() const
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
POD_CLASS public_key
Definition: crypto.h:76
const char * buf
Definition: slow_memmem.cpp:74
std::string get_english_name_for(const std::string &name)
Returns the name of a language in English.
version
Supported socks variants.
Definition: socks.h:57
void set_default_decimal_point(unsigned int decimal_point)
#define MWARNING(x)
Definition: misc_log_ex.h:74
static bool wallet_valid_path_format(const std::string &file_path)
Definition: wallet2.cpp:5707
void start_auto_config(const multisig_wallet_state &state)
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
This is the orginal cryptonote protocol network-events handler, modified by us.
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
static bool parse_short_payment_id(const std::string &payment_id_str, crypto::hash8 &payment_id)
Definition: wallet2.cpp:5725
#define PRINT_USAGE(usage_help)
static bool install(T t)
installs a signal handler
Definition: util.h:164
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
bool get_processable_messages(const multisig_wallet_state &state, bool force_sync, std::vector< processing_data > &data_list, std::string &wait_reason)
const std::string & reason() const
account_public_address addr
std::string blobdata
Definition: blobdatatype.h:39
static std::pair< std::unique_ptr< wallet2 >, password_container > make_new(const boost::program_options::variables_map &vm, bool unattended, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors...
Definition: wallet2.cpp:1250
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
boost::optional< std::pair< uint32_t, uint32_t > > parse_subaddress_lookahead(const std::string &str)
Definition: util.cpp:977
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_file(const boost::program_options::variables_map &vm, bool unattended, const std::string &wallet_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet_file if no errors.
Definition: wallet2.cpp:1233
scoped_message_writer success_msg_writer(bool color=true)
bool get_signer_index_by_label(const std::string label, uint32_t &index) const
static const char * message_state_to_string(message_state state)
unsigned char data[64]
Definition: secp256k1.h:75
expect< void > success() noexcept
Definition: expect.h:397
std::pair< boost::optional< boost::program_options::variables_map >, bool > main(int argc, char **argv, const char *const usage, const char *const notice, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description &positional_options, const std::function< void(const std::string &, bool)> &print, const char *default_log_name, bool log_to_console)
Definition: wallet_args.cpp:86
bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional< public_key > &B, const public_key &D, const signature &sig)
Definition: crypto.h:314
POD_CLASS signature
Definition: crypto.h:108
scoped_message_writer fail_msg_writer()
Mainly useful to represent current progress of application.
POD_CLASS hash8
Definition: hash.h:53
const T & move(const T &t)
Definition: gtest-port.h:1317
void set_signer(const multisig_wallet_state &state, uint32_t index, const boost::optional< std::string > &label, const boost::optional< std::string > &transport_address, const boost::optional< cryptonote::account_public_address > etn_address)
std::string original
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
#define PRIu64
Definition: inttypes.h:142
#define ENDL
Definition: misc_log_ex.h:149
#define PAUSE_READLINE()
Definition: blake256.h:37
std::string buff_to_hex_nodelimer(const std::string &src)
Definition: string_tools.h:87
bool get_auto_send() const
POD_CLASS key_image
Definition: crypto.h:102
static const char * tr(const char *str)
Definition: wallet2.cpp:994
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1225
void * memcpy(void *a, const void *b, size_t c)
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:430
void process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1173
crypto::key_image k_image
std::string content
#define DEFAULT_MIX
crypto::hash get_transaction_hash(const transaction &t)
std::string signer_to_string(const authorized_signer &signer, uint32_t max_width)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
signed __int64 int64_t
Definition: stdint.h:135
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
bool signer_config_complete() const
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1178
Mnemonic seed generation and wallet restoration from them.
T & unwrap(mlocked< T > &src)
Definition: mlocker.h:80
crypto::key_image m_key_image
Definition: wallet2.h:311
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:53
bool process_command_vec(const std::vector< std::string > &cmd)
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
std::string to_string() const
std::set< uint32_t > m_subaddr_indices
Definition: wallet2.h:405
#define F(s)
const cryptonote::transaction & tx() const
bool get_active() const
bool get_message_by_id(uint32_t id, message &m) const
uint32_t get_num_authorized_signers() const
std::vector< uint32_t > message_ids
expect< void > send(const epee::span< const std::uint8_t > payload, void *const socket, const int flags) noexcept
Definition: zmq.cpp:182
void set_message_processed_or_sent(uint32_t id)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
POD_CLASS hash
Definition: hash.h:50
const command_line::arg_descriptor< uint64_t > arg_fallback_to_pow_checkpoint_height
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height)
int main(int argc, char *argv[])
const char * address
Definition: multisig.cpp:37
uint16_t const RPC_DEFAULT_PORT
const command_line::arg_descriptor< std::string > arg_fallback_to_pow_checkpoint_hash
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
Definition: crypto.h:262
const char * spendkey
Definition: multisig.cpp:38
std::string to_string(t_connection_type type)
std::string hex(difficulty_type v)
Definition: difficulty.cpp:254
console_colors
Definition: misc_log_ex.h:206
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
constexpr const char ETN_DONATION_ADDR[]
Definition: simplewallet.h:56
else if(0==res)
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
bool run_handling(std::function< std::string(void)> prompt, const std::string &usage_string, std::function< void(void)> exit_handler=NULL)
std::vector< pending_tx > m_ptx
Definition: wallet2.h:512
scoped_message_writer msg_writer(epee::console_colors color=epee::console_color_default)
const char * data() const noexcept
#define HF_VERSION_PER_BYTE_FEE
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
Definition: string_tools.h:125
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
message_state state
uint32_t round
void set_auto_send(bool auto_send)
message_direction direction
#define CATCH_ENTRY_L0(lacation, return_val)
Definition: misc_log_ex.h:165
#define LONG_PAYMENT_ID_SUPPORT_CHECK()
error
Tracks LMDB error codes.
Definition: error.h:44
void mlog_set_log_level(int level)
Definition: mlog.cpp:282
SECP256K1_API secp256k1_context * secp256k1_context_create(unsigned int flags) SECP256K1_WARN_UNUSED_RESULT
#define CORE_RPC_VERSION_MAJOR
size_t add_message(const multisig_wallet_state &state, uint32_t signer_index, message_type type, message_direction direction, const std::string &content)
const crypto::public_key & get_public_key() const
Definition: wallet2.h:326
std::string get_human_readable_timestamp(uint64_t ts)
Definition: util.cpp:1077
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
Definition: string_tools.h:92
void init(const multisig_wallet_state &state, const std::string &own_label, const std::string &own_transport_address, uint32_t num_authorized_signers, uint32_t num_required_signers)
unsigned char u8
Definition: chacha_private.h:9
static bool parse_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition: wallet2.cpp:5738
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:265
uint64_t sent
void verbose(enum verbosity_value level, const char *format,...) ATTR_FORMAT(printf
std::vector< size_t > selected_transfers
Definition: wallet2.h:425
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content)
uint8_t threshold
Definition: blockchain.cpp:92