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"
75 
76 #ifdef WIN32
77 #include <boost/locale.hpp>
78 #include <boost/filesystem.hpp>
79 #endif
80 
81 #ifdef HAVE_READLINE
82 #include "readline_buffer.h"
83 #endif
84 
85 using namespace std;
86 using namespace epee;
87 using namespace cryptonote;
88 using boost::lexical_cast;
89 namespace po = boost::program_options;
91 
92 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
93 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.simplewallet"
94 
95 #define EXTENDED_LOGS_FILE "wallet_details.log"
96 
97 #define DEFAULT_MIX 0
98 
99 #define MIN_RING_SIZE 1 // Used to inform user about min ring size -- does not track actual protocol
100 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
101 
102 #define LOCK_IDLE_SCOPE() \
103  bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
104  m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
105  /* stop any background refresh, and take over */ \
106  m_wallet->stop(); \
107  boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
108  m_idle_cond.notify_all(); \
109  epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
110  m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
111  })
112 
113 #define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \
114  LOCK_IDLE_SCOPE(); \
115  boost::optional<tools::password_container> pwd_container = boost::none; \
116  if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \
117  tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
118 
119 #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;)
120 
121 #define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help;
122 
123 #define LONG_PAYMENT_ID_SUPPORT_CHECK() \
124  do { \
125  if (!m_long_payment_id_support) { \
126  fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \
127  fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \
128  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."); \
129  return true; \
130  } \
131  } while(0)
132 
136 };
137 
138 namespace
139 {
140  const std::array<const char* const, 5> allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}};
142  const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
143  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>"), ""};
144  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"), ""};
145  const command_line::arg_descriptor<std::string> arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""};
146  const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
147  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"), ""};
149  const command_line::arg_descriptor<std::string> arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""};
150  const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
151  const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
152  const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
153  const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false};
154  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};
155  const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0};
156  const command_line::arg_descriptor<std::string> arg_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""};
157  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};
158  const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false};
159  const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""};
160  const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false};
161  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};
162  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};
163  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};
164 
166 
167  const command_line::arg_descriptor<uint32_t> arg_account_major_offset = {"account-major-offset", sw::tr("Account Index Offset"), 0};
168 
169  const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
170  const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]");
171  const char* USAGE_SHOW_BALANCE("balance [detail]");
172  const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
173  const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
174  const char* USAGE_PAYMENT_ID("payment_id");
175  const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
176  const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
177  const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
178  const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
179  const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
180  const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
181  const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]");
182  const char* USAGE_SIGN_TRANSFER("sign_transfer [export_raw]");
183  const char* USAGE_SET_LOG("set_log <level>|{+,-,}<categories>");
184  const char* USAGE_ACCOUNT("account\n"
185  " account new <label text with white spaces allowed>\n"
186  " account switch <index> \n"
187  " account label <index> <label text with white spaces allowed>\n"
188  " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
189  " account untag <account_index_1> [<account_index_2> ...]\n"
190  " account tag_description <tag_name> <description>");
191  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>]");
192  const char* USAGE_INTEGRATED_ADDRESS("integrated_address [<payment_id> | <address>]");
193  const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
194  const char* USAGE_SET_VARIABLE("set <option> [<value>]");
195  const char* USAGE_GET_TX_KEY("get_tx_key <txid>");
196  const char* USAGE_SET_TX_KEY("set_tx_key <txid> <tx_key>");
197  const char* USAGE_CHECK_TX_KEY("check_tx_key <txid> <txkey> <address>");
198  const char* USAGE_GET_TX_PROOF("get_tx_proof <txid> <address> [<message>]");
199  const char* USAGE_CHECK_TX_PROOF("check_tx_proof <txid> <address> <signature_file> [<message>]");
200  const char* USAGE_GET_SPEND_PROOF("get_spend_proof <txid> [<message>]");
201  const char* USAGE_CHECK_SPEND_PROOF("check_spend_proof <txid> <signature_file> [<message>]");
202  const char* USAGE_GET_RESERVE_PROOF("get_reserve_proof (all|<amount>) [<message>]");
203  const char* USAGE_CHECK_RESERVE_PROOF("check_reserve_proof <address> <signature_file> [<message>]");
204  const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
205  const char* USAGE_UNSPENT_OUTPUTS("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]");
206  const char* USAGE_RESCAN_BC("rescan_bc [hard|soft|keep_ki] [start_height=0]");
207  const char* USAGE_SET_TX_NOTE("set_tx_note <txid> [free text note]");
208  const char* USAGE_GET_TX_NOTE("get_tx_note <txid>");
209  const char* USAGE_GET_DESCRIPTION("get_description");
210  const char* USAGE_SET_DESCRIPTION("set_description [free text note]");
211  const char* USAGE_SIGN("sign <filename>");
212  const char* USAGE_VERIFY("verify <filename> <address> <signature>");
213  const char* USAGE_EXPORT_KEY_IMAGES("export_key_images <filename>");
214  const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>");
215  const char* USAGE_HW_KEY_IMAGES_SYNC("hw_key_images_sync");
216  const char* USAGE_HW_RECONNECT("hw_reconnect");
217  const char* USAGE_EXPORT_OUTPUTS("export_outputs <filename>");
218  const char* USAGE_IMPORT_OUTPUTS("import_outputs <filename>");
219  const char* USAGE_SHOW_TRANSFER("show_transfer <txid>");
220  const char* USAGE_MAKE_MULTISIG("make_multisig <threshold> <string1> [<string>...]");
221  const char* USAGE_FINALIZE_MULTISIG("finalize_multisig <string> [<string>...]");
222  const char* USAGE_EXCHANGE_MULTISIG_KEYS("exchange_multisig_keys <string> [<string>...]");
223  const char* USAGE_EXPORT_MULTISIG_INFO("export_multisig_info <filename>");
224  const char* USAGE_IMPORT_MULTISIG_INFO("import_multisig_info <filename> [<filename>...]");
225  const char* USAGE_SIGN_MULTISIG("sign_multisig <filename>");
226  const char* USAGE_SUBMIT_MULTISIG("submit_multisig <filename>");
227  const char* USAGE_EXPORT_RAW_MULTISIG_TX("export_raw_multisig_tx <filename>");
228  const char* USAGE_MMS("mms [<subcommand> [<subcommand_parameters>]]");
229  const char* USAGE_MMS_INIT("mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
230  const char* USAGE_MMS_INFO("mms info");
231  const char* USAGE_MMS_SIGNER("mms signer [<number> <label> [<transport_address> [<etn_address>]]]");
232  const char* USAGE_MMS_LIST("mms list");
233  const char* USAGE_MMS_NEXT("mms next [sync]");
234  const char* USAGE_MMS_SYNC("mms sync");
235  const char* USAGE_MMS_TRANSFER("mms transfer <transfer_command_arguments>");
236  const char* USAGE_MMS_DELETE("mms delete (<message_id> | all)");
237  const char* USAGE_MMS_SEND("mms send [<message_id>]");
238  const char* USAGE_MMS_RECEIVE("mms receive");
239  const char* USAGE_MMS_EXPORT("mms export <message_id>");
240  const char* USAGE_MMS_NOTE("mms note [<label> <text>]");
241  const char* USAGE_MMS_SHOW("mms show <message_id>");
242  const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]");
243  const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config");
244  const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]");
245  const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config");
246  const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>");
247  const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>");
248  const char* USAGE_SET_RING("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )");
249  const char* USAGE_UNSET_RING("unset_ring <txid> | ( <key_image> [<key_image>...] )");
250  const char* USAGE_SAVE_KNOWN_RINGS("save_known_rings");
251  const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]");
252  const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>");
253  const char* USAGE_IS_OUTPUT_SPENT("is_output_spent <amount>/<offset>");
254  const char* USAGE_FREEZE("freeze <key_image>");
255  const char* USAGE_THAW("thaw <key_image>");
256  const char* USAGE_FROZEN("frozen <key_image>");
257  const char* USAGE_NET_STATS("net_stats");
258  const char* USAGE_WELCOME("welcome");
259  const char* USAGE_VERSION("version");
260  const char* USAGE_HELP("help [<command>]");
261 
262  std::string input_line(const std::string& prompt, bool yesno = false)
263  {
264 #ifdef HAVE_READLINE
265  rdln::suspend_readline pause_readline;
266 #endif
267  std::cout << prompt;
268  if (yesno)
269  std::cout << " (Y/Yes/N/No)";
270  std::cout << ": " << std::flush;
271 
273 #ifdef _WIN32
274  buf = tools::input_line_win();
275 #else
276  std::getline(std::cin, buf);
277 #endif
278 
280  }
281 
282  epee::wipeable_string input_secure_line(const char *prompt)
283  {
284 #ifdef HAVE_READLINE
285  rdln::suspend_readline pause_readline;
286 #endif
287  auto pwd_container = tools::password_container::prompt(false, prompt, false);
288  if (!pwd_container)
289  {
290  MERROR("Failed to read secure line");
291  return "";
292  }
293 
294  epee::wipeable_string buf = pwd_container->password();
295 
296  buf.trim();
297  return buf;
298  }
299 
300  boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
301  {
302 #ifdef HAVE_READLINE
303  rdln::suspend_readline pause_readline;
304 #endif
305  auto pwd_container = tools::password_container::prompt(verify, prompt);
306  if (!pwd_container)
307  {
308  tools::fail_msg_writer() << sw::tr("failed to read wallet password");
309  }
310  return pwd_container;
311  }
312 
313  boost::optional<tools::password_container> default_password_prompter(bool verify)
314  {
315  return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify);
316  }
317 
318  inline std::string interpret_rpc_response(bool ok, const std::string& status)
319  {
320  std::string err;
321  if (ok)
322  {
323  if (status == CORE_RPC_STATUS_BUSY)
324  {
325  err = sw::tr("daemon is busy. Please try again later.");
326  }
327  else if (status != CORE_RPC_STATUS_OK)
328  {
329  err = status;
330  }
331  }
332  else
333  {
334  err = sw::tr("possibly lost connection to daemon");
335  }
336  return err;
337  }
338 
340  {
342  }
343 
344  tools::scoped_message_writer message_writer(epee::console_colors color = epee::console_color_default, bool bright = false)
345  {
346  return tools::scoped_message_writer(color, bright);
347  }
348 
350  {
352  }
353 
354  bool parse_bool(const std::string& s, bool& result)
355  {
356  if (s == "1" || command_line::is_yes(s))
357  {
358  result = true;
359  return true;
360  }
361  if (s == "0" || command_line::is_no(s))
362  {
363  result = false;
364  return true;
365  }
366 
367  boost::algorithm::is_iequal ignore_case{};
368  if (boost::algorithm::equals("true", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case))
369  {
370  result = true;
371  return true;
372  }
373  if (boost::algorithm::equals("false", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("false"), s, ignore_case))
374  {
375  result = false;
376  return true;
377  }
378 
379  return false;
380  }
381 
382  template <typename F>
383  bool parse_bool_and_use(const std::string& s, F func)
384  {
385  bool r;
386  if (parse_bool(s, r))
387  {
388  func(r);
389  return true;
390  }
391  else
392  {
393  fail_msg_writer() << sw::tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
394  return false;
395  }
396  }
397 
398  const struct
399  {
400  const char *name;
401  tools::wallet2::RefreshType refresh_type;
402  } refresh_type_names[] =
403  {
404  { "full", tools::wallet2::RefreshFull },
405  { "optimize-coinbase", tools::wallet2::RefreshOptimizeCoinbase },
406  { "optimized-coinbase", tools::wallet2::RefreshOptimizeCoinbase },
407  { "no-coinbase", tools::wallet2::RefreshNoCoinbase },
408  { "default", tools::wallet2::RefreshDefault },
409  };
410 
411  bool parse_refresh_type(const std::string &s, tools::wallet2::RefreshType &refresh_type)
412  {
413  for (size_t n = 0; n < sizeof(refresh_type_names) / sizeof(refresh_type_names[0]); ++n)
414  {
415  if (s == refresh_type_names[n].name)
416  {
417  refresh_type = refresh_type_names[n].refresh_type;
418  return true;
419  }
420  }
421  fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse refresh type");
422  return false;
423  }
424 
425  std::string get_refresh_type_name(tools::wallet2::RefreshType type)
426  {
427  for (size_t n = 0; n < sizeof(refresh_type_names) / sizeof(refresh_type_names[0]); ++n)
428  {
429  if (type == refresh_type_names[n].refresh_type)
430  return refresh_type_names[n].name;
431  }
432  return "invalid";
433  }
434 
435  std::string get_version_string(uint32_t version)
436  {
437  return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff);
438  }
439 
440  std::string oa_prompter(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)
441  {
442  if (addresses.empty())
443  return {};
444  // prompt user for confirmation.
445  // inform user of DNSSEC validation status as well.
446  std::string dnssec_str;
447  if (dnssec_valid)
448  {
449  dnssec_str = sw::tr("DNSSEC validation passed");
450  }
451  else
452  {
453  dnssec_str = sw::tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
454  }
455  std::stringstream prompt;
456  prompt << sw::tr("For URL: ") << url
457  << ", " << dnssec_str << std::endl
458  << sw::tr(" Electroneum Address = ") << addresses[0]
459  << std::endl
460  << sw::tr("Is this OK?")
461  ;
462  // prompt the user for confirmation given the dns query and dnssec status
463  std::string confirm_dns_ok = input_line(prompt.str(), true);
464  if (std::cin.eof())
465  {
466  return {};
467  }
468  if (!command_line::is_yes(confirm_dns_ok))
469  {
470  std::cout << sw::tr("you have cancelled the transfer request") << std::endl;
471  return {};
472  }
473  return addresses[0];
474  }
475 
476  bool parse_subaddress_indices(const std::string& arg, std::set<uint32_t>& subaddr_indices)
477  {
478  subaddr_indices.clear();
479 
480  if (arg.substr(0, 6) != "index=")
481  return false;
482  std::string subaddr_indices_str_unsplit = arg.substr(6, arg.size() - 6);
483  std::vector<std::string> subaddr_indices_str;
484  boost::split(subaddr_indices_str, subaddr_indices_str_unsplit, boost::is_any_of(","));
485 
486  for (const auto& subaddr_index_str : subaddr_indices_str)
487  {
488  uint32_t subaddr_index;
489  if(!epee::string_tools::get_xtype_from_string(subaddr_index, subaddr_index_str))
490  {
491  fail_msg_writer() << sw::tr("failed to parse index: ") << subaddr_index_str;
492  subaddr_indices.clear();
493  return false;
494  }
495  subaddr_indices.insert(subaddr_index);
496  }
497  return true;
498  }
499 
500  boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
501  {
502  auto r = tools::parse_subaddress_lookahead(str);
503  if (!r)
504  fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be <major>:<minor>");
505  return r;
506  }
507 
508  void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon)
509  {
510  bool warn_of_possible_attack = !trusted_daemon;
511  try
512  {
513  std::rethrow_exception(e);
514  }
515  catch (const tools::error::daemon_busy&)
516  {
517  fail_msg_writer() << sw::tr("daemon is busy. Please try again later.");
518  }
520  {
521  fail_msg_writer() << sw::tr("no connection to daemon. Please make sure daemon is running.");
522  }
523  catch (const tools::error::wallet_rpc_error& e)
524  {
525  LOG_ERROR("RPC error: " << e.to_string());
526  fail_msg_writer() << sw::tr("RPC error: ") << e.what();
527  }
528  catch (const tools::error::get_outs_error &e)
529  {
530  fail_msg_writer() << sw::tr("failed to get random outputs to mix: ") << e.what();
531  }
533  {
534  LOG_PRINT_L0(boost::format("not enough ETN to transfer, available only %s, sent amount %s") %
535  print_etn(e.available()) %
536  print_etn(e.tx_amount()));
537  fail_msg_writer() << sw::tr("Not enough ETN in unlocked balance");
538  warn_of_possible_attack = false;
539  }
540  catch (const tools::error::not_enough_etn& e)
541  {
542  LOG_PRINT_L0(boost::format("not enough ETN to transfer, available only %s, sent amount %s") %
543  print_etn(e.available()) %
544  print_etn(e.tx_amount()));
545  fail_msg_writer() << sw::tr("Not enough ETN in unlocked balance");
546  warn_of_possible_attack = false;
547  }
548  catch (const tools::error::tx_not_possible& e)
549  {
550  LOG_PRINT_L0(boost::format("not enough ETN to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
551  print_etn(e.available()) %
552  print_etn(e.tx_amount() + e.fee()) %
553  print_etn(e.tx_amount()) %
554  print_etn(e.fee()));
555  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");
556  warn_of_possible_attack = false;
557  }
558  catch (const tools::error::not_enough_outs_to_mix& e)
559  {
560  auto writer = fail_msg_writer();
561  writer << sw::tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
562  for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
563  {
564  writer << "\n" << sw::tr("output amount") << " = " << print_etn(outs_for_amount.first) << ", " << sw::tr("found outputs to use") << " = " << outs_for_amount.second;
565  }
566  writer << sw::tr("Please use sweep_unmixable.");
567  }
568  catch (const tools::error::tx_not_constructed&)
569  {
570  fail_msg_writer() << sw::tr("transaction was not constructed");
571  warn_of_possible_attack = false;
572  }
573  catch (const tools::error::tx_rejected& e)
574  {
575  fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon")) % get_transaction_hash(e.tx()));
576  std::string reason = e.reason();
577  if (!reason.empty())
578  fail_msg_writer() << sw::tr("Reason: ") << reason;
579  }
580  catch (const tools::error::tx_sum_overflow& e)
581  {
582  fail_msg_writer() << e.what();
583  warn_of_possible_attack = false;
584  }
585  catch (const tools::error::zero_destination&)
586  {
587  fail_msg_writer() << sw::tr("one of destinations is zero");
588  warn_of_possible_attack = false;
589  }
590  catch (const tools::error::tx_too_big& e)
591  {
592  fail_msg_writer() << sw::tr("failed to find a suitable way to split transactions");
593  warn_of_possible_attack = false;
594  }
595  catch (const tools::error::transfer_error& e)
596  {
597  LOG_ERROR("unknown transfer error: " << e.to_string());
598  fail_msg_writer() << sw::tr("unknown transfer error: ") << e.what();
599  }
600  catch (const tools::error::multisig_export_needed& e)
601  {
602  LOG_ERROR("Multisig error: " << e.to_string());
603  fail_msg_writer() << sw::tr("Multisig error: ") << e.what();
604  warn_of_possible_attack = false;
605  }
606  catch (const tools::error::wallet_internal_error& e)
607  {
608  LOG_ERROR("internal error: " << e.to_string());
609  fail_msg_writer() << sw::tr("internal error: ") << e.what();
610  }
611  catch (const std::exception& e)
612  {
613  LOG_ERROR("unexpected error: " << e.what());
614  fail_msg_writer() << sw::tr("unexpected error: ") << e.what();
615  }
616 
617  if (warn_of_possible_attack)
618  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.");
619  }
620 
621  bool check_file_overwrite(const std::string &filename)
622  {
623  boost::system::error_code errcode;
624  if (boost::filesystem::exists(filename, errcode))
625  {
626  if (boost::ends_with(filename, ".keys"))
627  {
628  fail_msg_writer() << boost::format(sw::tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
629  return false;
630  }
631  return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it?")) % filename).str(), true));
632  }
633  return true;
634  }
635 
636  void print_secret_key(const crypto::secret_key &k)
637  {
638  static constexpr const char hex[] = u8"0123456789abcdef";
639  const uint8_t *ptr = (const uint8_t*)k.data;
640  for (size_t i = 0, sz = sizeof(k); i < sz; ++i)
641  {
642  putchar(hex[*ptr >> 4]);
643  putchar(hex[*ptr & 15]);
644  ++ptr;
645  }
646  }
647 }
648 
649 bool parse_priority(const std::string& arg, uint32_t& priority)
650 {
651  auto priority_pos = std::find(
652  allowed_priority_strings.begin(),
653  allowed_priority_strings.end(),
654  arg);
655  if(priority_pos != allowed_priority_strings.end()) {
656  priority = std::distance(allowed_priority_strings.begin(), priority_pos);
657  return true;
658  }
659  return false;
660 }
661 
662 std::string join_priority_strings(const char *delimiter)
663 {
664  std::string s;
665  for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
666  {
667  if (!s.empty())
668  s += delimiter;
669  s += allowed_priority_strings[n];
670  }
671  return s;
672 }
673 
674 std::string simple_wallet::get_commands_str()
675 {
676  std::stringstream ss;
677  ss << tr("Commands: ") << ENDL;
678  std::string usage = m_cmd_binder.get_usage();
679  boost::replace_all(usage, "\n", "\n ");
680  usage.insert(0, " ");
681  ss << usage << ENDL;
682  return ss.str();
683 }
684 
685 std::string simple_wallet::get_command_usage(const std::vector<std::string> &args)
686 {
687  std::pair<std::string, std::string> documentation = m_cmd_binder.get_documentation(args);
688  std::stringstream ss;
689  if(documentation.first.empty())
690  {
691  ss << tr("Unknown command: ") << args.front();
692  }
693  else
694  {
695  std::string usage = documentation.second.empty() ? args.front() : documentation.first;
696  std::string description = documentation.second.empty() ? documentation.first : documentation.second;
697  usage.insert(0, " ");
698  ss << tr("Command usage: ") << ENDL << usage << ENDL << ENDL;
699  boost::replace_all(description, "\n", "\n ");
700  description.insert(0, " ");
701  ss << tr("Command description: ") << ENDL << description << ENDL;
702  }
703  return ss.str();
704 }
705 
706 bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
707 {
708  // don't log
709  PAUSE_READLINE();
710  if (m_wallet->key_on_device()) {
711  std::cout << "secret: On device. Not available" << std::endl;
712  } else {
714  printf("secret: ");
715  print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
716  putchar('\n');
717  }
718  std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
719 
720  return true;
721 }
722 
723 bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
724 {
725  if (m_wallet->watch_only())
726  {
727  fail_msg_writer() << tr("wallet is watch-only and has no spend key");
728  return true;
729  }
730  // don't log
731  PAUSE_READLINE();
732  if (m_wallet->key_on_device()) {
733  std::cout << "secret: On device. Not available" << std::endl;
734  } else {
736  printf("secret: ");
737  print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
738  putchar('\n');
739  }
740  std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
741 
742  return true;
743 }
744 
745 bool simple_wallet::print_seed(bool encrypted)
746 {
747  bool success = false;
749  bool ready, multisig;
750 
751  if (m_wallet->key_on_device())
752  {
753  fail_msg_writer() << tr("command not supported by HW wallet");
754  return true;
755  }
756  if (m_wallet->watch_only())
757  {
758  fail_msg_writer() << tr("wallet is watch-only and has no seed");
759  return true;
760  }
761 
762  multisig = m_wallet->multisig(&ready);
763  if (multisig)
764  {
765  if (!ready)
766  {
767  fail_msg_writer() << tr("wallet is multisig but not yet finalized");
768  return true;
769  }
770  }
771 
773 
774  if (!multisig && !m_wallet->is_deterministic())
775  {
776  fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
777  return true;
778  }
779 
780  epee::wipeable_string seed_pass;
781  if (encrypted)
782  {
783  auto pwd_container = password_prompter(tr("Enter optional seed offset passphrase, empty to see raw seed"), true);
784  if (std::cin.eof() || !pwd_container)
785  return true;
786  seed_pass = pwd_container->password();
787  }
788 
789  if (multisig)
790  success = m_wallet->get_multisig_seed(seed, seed_pass);
791  else if (m_wallet->is_deterministic())
792  success = m_wallet->get_seed(seed, seed_pass);
793 
794  if (success)
795  {
796  print_seed(seed);
797  }
798  else
799  {
800  fail_msg_writer() << tr("Failed to retrieve seed");
801  }
802  return true;
803 }
804 
805 bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
806 {
807  return print_seed(false);
808 }
809 
810 bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
811 {
812  return print_seed(true);
813 }
814 
815 bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
816 {
817  if (m_wallet->key_on_device())
818  {
819  fail_msg_writer() << tr("command not supported by HW wallet");
820  return true;
821  }
822  if (m_wallet->multisig())
823  {
824  fail_msg_writer() << tr("wallet is multisig and has no seed");
825  return true;
826  }
827  if (m_wallet->watch_only())
828  {
829  fail_msg_writer() << tr("wallet is watch-only and has no seed");
830  return true;
831  }
832 
833  epee::wipeable_string password;
834  {
836 
837  if (!m_wallet->is_deterministic())
838  {
839  fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
840  return true;
841  }
842 
843  // we need the password, even if ask-password is unset
844  if (!pwd_container)
845  {
846  pwd_container = get_and_verify_password();
847  if (pwd_container == boost::none)
848  {
849  fail_msg_writer() << tr("Incorrect password");
850  return true;
851  }
852  }
853  password = pwd_container->password();
854  }
855 
856  std::string mnemonic_language = get_mnemonic_language();
857  if (mnemonic_language.empty())
858  return true;
859 
860  m_wallet->set_seed_language(std::move(mnemonic_language));
861  m_wallet->rewrite(m_wallet_file, password);
862  return true;
863 }
864 
865 bool simple_wallet::change_password(const std::vector<std::string> &args)
866 {
867  const auto orig_pwd_container = get_and_verify_password();
868 
869  if(orig_pwd_container == boost::none)
870  {
871  fail_msg_writer() << tr("Your original password was incorrect.");
872  return true;
873  }
874 
875  // prompts for a new password, pass true to verify the password
876  const auto pwd_container = default_password_prompter(true);
877  if(!pwd_container)
878  return true;
879 
880  try
881  {
882  m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
883  }
884  catch (const tools::error::wallet_logic_error& e)
885  {
886  fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what();
887  return true;
888  }
889 
890  return true;
891 }
892 
893 bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
894 {
896 
897  crypto::hash payment_id;
898  if (args.size() > 0)
899  {
900  PRINT_USAGE(USAGE_PAYMENT_ID);
901  return true;
902  }
903  payment_id = crypto::rand<crypto::hash>();
904  success_msg_writer() << tr("Random payment ID: ") << payment_id;
905  return true;
906 }
907 
908 bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
909 {
910  if (!try_connect_to_daemon())
911  return true;
912  const bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE);
913  const uint64_t base_fee = m_wallet->get_base_fee();
914  const char *base = per_byte ? "byte" : "kB";
915  const uint64_t typical_size = per_byte ? 2500 : 13;
916  const uint64_t size_granularity = per_byte ? 1 : 1024;
917  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();
918 
919  std::vector<uint64_t> fees;
920  for (uint32_t priority = 1; priority <= 4; ++priority)
921  {
922  uint64_t mult = m_wallet->get_fee_multiplier(priority);
923  fees.push_back(base_fee * typical_size * mult);
924  }
925  std::vector<std::pair<uint64_t, uint64_t>> blocks;
926  try
927  {
928  uint64_t base_size = typical_size * size_granularity;
929  blocks = m_wallet->estimate_backlog(base_size, base_size + size_granularity - 1, fees);
930  }
931  catch (const std::exception &e)
932  {
933  fail_msg_writer() << tr("Error: failed to estimate backlog array size: ") << e.what();
934  return true;
935  }
936  if (blocks.size() != 4)
937  {
938  fail_msg_writer() << tr("Error: bad estimated backlog array size");
939  return true;
940  }
941 
942  for (uint32_t priority = 1; priority <= 4; ++priority)
943  {
944  uint64_t nblocks_low = blocks[priority - 1].first;
945  uint64_t nblocks_high = blocks[priority - 1].second;
946  if (nblocks_low > 0)
947  {
948  std::string msg;
949  if (priority == m_wallet->get_default_priority() || (m_wallet->get_default_priority() == 0 && priority == 2))
950  msg = tr(" (current)");
951  uint64_t minutes_low = nblocks_low * DIFFICULTY_TARGET_V6 / 60, minutes_high = nblocks_high * DIFFICULTY_TARGET_V6 / 60;
952  if (nblocks_high == nblocks_low)
953  message_writer() << (boost::format(tr("%u block (%u minutes) backlog at priority %u%s")) % nblocks_low % minutes_low % priority % msg).str();
954  else
955  message_writer() << (boost::format(tr("%u to %u block (%u to %u minutes) backlog at priority %u")) % nblocks_low % nblocks_high % minutes_low % minutes_high % priority).str();
956  }
957  else
958  message_writer() << tr("No backlog at priority ") << priority;
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");
5545  }
5546  catch (const tools::error::wallet_rpc_error& e)
5547  {
5548  LOG_ERROR("RPC error: " << e.to_string());
5549  fail_msg_writer() << tr("RPC error: ") << e.what();
5550  }
5551  catch (const std::exception& e)
5552  {
5553  LOG_ERROR("unexpected error: " << e.what());
5554  fail_msg_writer() << tr("unexpected error: ") << e.what();
5555  }
5556  catch (...)
5557  {
5558  LOG_ERROR("unknown error");
5559  fail_msg_writer() << tr("unknown error");
5560  }
5561 
5562  return true;
5563 }
5564 //----------------------------------------------------------------------------------------------------
5565 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
5566 {
5567  std::stringstream ostr;
5568 
5569  for (uint64_t h: heights)
5570  blockchain_height = std::max(blockchain_height, h);
5571 
5572  for (size_t j = 0; j < heights.size(); ++j)
5573  ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j];
5574 
5575  // visualize the distribution, using the code by moneroexamples onion-monero-viewer
5576  const uint64_t resolution = 79;
5577  std::string ring_str(resolution, '_');
5578  for (size_t j = 0; j < heights.size(); ++j)
5579  {
5580  uint64_t pos = (heights[j] * resolution) / blockchain_height;
5581  ring_str[pos] = 'o';
5582  }
5583  if (highlight_height < blockchain_height)
5584  {
5585  uint64_t pos = (highlight_height * resolution) / blockchain_height;
5586  ring_str[pos] = '*';
5587  }
5588 
5589  return std::make_pair(ostr.str(), ring_str);
5590 }
5591 //----------------------------------------------------------------------------------------------------
5592 bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
5593 {
5594  uint32_t version;
5595  if (!try_connect_to_daemon(false, &version))
5596  return false;
5597  // available for RPC version 1.4 or higher
5598  if (version < MAKE_CORE_RPC_VERSION(1, 4))
5599  return true;
5600  std::string err;
5601  uint64_t blockchain_height = get_daemon_blockchain_height(err);
5602  if (!err.empty())
5603  {
5604  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
5605  return false;
5606  }
5607  // for each transaction
5608  for (size_t n = 0; n < ptx_vector.size(); ++n)
5609  {
5610  const cryptonote::transaction& tx = ptx_vector[n].tx;
5611  const tools::wallet2::tx_construction_data& construction_data = ptx_vector[n].construction_data;
5612  ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx);
5613  // for each input
5614  std::vector<uint64_t> spent_key_height(tx.vin.size());
5615  std::vector<crypto::hash> spent_key_txid (tx.vin.size());
5616  for (size_t i = 0; i < tx.vin.size(); ++i)
5617  {
5618  if (tx.vin[i].type() != typeid(cryptonote::txin_to_key))
5619  continue;
5620  const cryptonote::txin_to_key& in_key = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
5621  const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(construction_data.selected_transfers[i]);
5622  const cryptonote::tx_source_entry *sptr = NULL;
5623  for (const auto &src: construction_data.sources)
5624  if (src.outputs[src.real_output].second.dest == td.get_public_key())
5625  sptr = &src;
5626  if (!sptr)
5627  {
5628  fail_msg_writer() << tr("failed to find construction data for tx input");
5629  return false;
5630  }
5631  const cryptonote::tx_source_entry& source = *sptr;
5632 
5633  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);
5634  // convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount
5635  std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets);
5636  // get block heights from which those ring member keys originated
5638  req.outputs.resize(absolute_offsets.size());
5639  for (size_t j = 0; j < absolute_offsets.size(); ++j)
5640  {
5641  req.outputs[j].amount = in_key.amount;
5642  req.outputs[j].index = absolute_offsets[j];
5643  }
5645  bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
5646  err = interpret_rpc_response(r, res.status);
5647  if (!err.empty())
5648  {
5649  fail_msg_writer() << tr("failed to get output: ") << err;
5650  return false;
5651  }
5652  // make sure that returned block heights are less than blockchain height
5653  for (auto& res_out : res.outs)
5654  {
5655  if (res_out.height >= blockchain_height)
5656  {
5657  fail_msg_writer() << tr("output key's originating block height shouldn't be higher than the blockchain height");
5658  return false;
5659  }
5660  }
5661  ostr << tr("\nOriginating block heights: ");
5662  spent_key_height[i] = res.outs[source.real_output].height;
5663  spent_key_txid [i] = res.outs[source.real_output].txid;
5664  std::vector<uint64_t> heights(absolute_offsets.size(), 0);
5665  uint64_t highlight_height = std::numeric_limits<uint64_t>::max();
5666  for (size_t j = 0; j < absolute_offsets.size(); ++j)
5667  {
5668  heights[j] = res.outs[j].height;
5669  if (j == source.real_output)
5670  highlight_height = heights[j];
5671  }
5672  std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
5673  ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
5674  }
5675  // warn if rings contain keys originating from the same tx or temporally very close block heights
5676  bool are_keys_from_same_tx = false;
5677  bool are_keys_from_close_height = false;
5678  for (size_t i = 0; i < tx.vin.size(); ++i) {
5679  for (size_t j = i + 1; j < tx.vin.size(); ++j)
5680  {
5681  if (spent_key_txid[i] == spent_key_txid[j])
5682  are_keys_from_same_tx = true;
5683  if (std::abs((int64_t)(spent_key_height[i] - spent_key_height[j])) < (int64_t)5)
5684  are_keys_from_close_height = true;
5685  }
5686  }
5687  if (are_keys_from_same_tx || are_keys_from_close_height)
5688  {
5689  ostr
5690  << tr("\nWarning: Some input keys being spent are from ")
5691  << (are_keys_from_same_tx ? tr("the same transaction") : tr("blocks that are temporally very close"))
5692  << tr(", which can break the anonymity of ring signature. Make sure this is intentional!");
5693  }
5694  ostr << ENDL;
5695  }
5696  return true;
5697 }
5698 //----------------------------------------------------------------------------------------------------
5699 bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms)
5700 {
5701 // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
5702  if (!try_connect_to_daemon())
5703  return false;
5704 
5705  std::vector<std::string> local_args = args_;
5706 
5707  std::set<uint32_t> subaddr_indices;
5708  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
5709  {
5710  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
5711  return false;
5712  local_args.erase(local_args.begin());
5713  }
5714 
5715  uint32_t priority = 0;
5716  if (local_args.size() > 0 && parse_priority(local_args[0], priority))
5717  local_args.erase(local_args.begin());
5718 
5719  priority = m_wallet->adjust_priority(priority);
5720 
5721  size_t fake_outs_count = 0;
5722  if(local_args.size() > 0) {
5723  size_t ring_size;
5724  if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
5725  {
5726  fake_outs_count = m_wallet->default_mixin();
5727  if (fake_outs_count == 0)
5728  fake_outs_count = DEFAULT_MIX;
5729  }
5730  else if (ring_size == 0)
5731  {
5732  fail_msg_writer() << tr("Ring size must not be 0");
5733  return false;
5734  }
5735  else
5736  {
5737  fake_outs_count = ring_size - 1;
5738  local_args.erase(local_args.begin());
5739  }
5740  }
5741 
5742  uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
5743  if (adjusted_fake_outs_count > fake_outs_count)
5744  {
5745  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();
5746  return false;
5747  }
5748  if (adjusted_fake_outs_count < fake_outs_count)
5749  {
5750  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();
5751  return false;
5752  }
5753 
5754  const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
5755  if(local_args.size() < min_args)
5756  {
5757  fail_msg_writer() << tr("wrong number of arguments");
5758  return false;
5759  }
5760 
5761  std::vector<uint8_t> extra;
5762  bool payment_id_seen = false;
5763  if (!local_args.empty())
5764  {
5765  std::string payment_id_str = local_args.back();
5766  crypto::hash payment_id;
5767  bool r = true;
5768  if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id))
5769  {
5771 
5772  std::string extra_nonce;
5773  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
5774  r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
5775  local_args.pop_back();
5776  payment_id_seen = true;
5777  message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead");
5778  }
5779  if(!r)
5780  {
5781  fail_msg_writer() << tr("payment id failed to encode");
5782  return false;
5783  }
5784  }
5785 
5786  uint64_t locked_blocks = 0;
5787  if (transfer_type == TransferLocked)
5788  {
5789  try
5790  {
5791  locked_blocks = boost::lexical_cast<uint64_t>(local_args.back());
5792  }
5793  catch (const std::exception &e)
5794  {
5795  fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back();
5796  return false;
5797  }
5798  if (locked_blocks > 1000000)
5799  {
5800  fail_msg_writer() << tr("Locked blocks too high, max 1000000 (Ëœ4 yrs)");
5801  return false;
5802  }
5803  local_args.pop_back();
5804  }
5805 
5806  vector<cryptonote::address_parse_info> dsts_info;
5807  vector<cryptonote::tx_destination_entry> dsts;
5808  size_t num_subaddresses = 0;
5809  for (size_t i = 0; i < local_args.size(); )
5810  {
5811  dsts_info.emplace_back();
5812  cryptonote::address_parse_info & info = dsts_info.back();
5814  bool r = true;
5815 
5816  // check for a URI
5817  std::string address_uri, payment_id_uri, tx_description, recipient_name, error;
5818  std::vector<std::string> unknown_parameters;
5819  uint64_t amount = 0;
5820  bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters, error);
5821  if (has_uri)
5822  {
5823  r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_uri, oa_prompter);
5824  if (payment_id_uri.size() == 16)
5825  {
5826  if (!tools::wallet2::parse_short_payment_id(payment_id_uri, info.payment_id))
5827  {
5828  fail_msg_writer() << tr("failed to parse short payment ID from URI");
5829  return false;
5830  }
5831  info.has_payment_id = true;
5832  }
5833  de.amount = amount;
5834  de.original = local_args[i];
5835  ++i;
5836  }
5837  else if (i + 1 < local_args.size())
5838  {
5839  r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter);
5840  bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
5841  if(!ok || 0 == de.amount)
5842  {
5843  fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] <<
5844  ", " << tr("expected number from 0 to ") << print_etn(std::numeric_limits<uint64_t>::max());
5845  return false;
5846  }
5847  de.original = local_args[i];
5848  i += 2;
5849  }
5850  else
5851  {
5852  if (boost::starts_with(local_args[i], "electroneum:"))
5853  fail_msg_writer() << tr("Invalid last argument: ") << local_args.back() << ": " << error;
5854  else
5855  fail_msg_writer() << tr("Invalid last argument: ") << local_args.back();
5856  return false;
5857  }
5858 
5859  if (!r)
5860  {
5861  fail_msg_writer() << tr("failed to parse address");
5862  return false;
5863  }
5864  de.addr = info.address;
5865  de.is_subaddress = info.is_subaddress;
5866  de.is_integrated = info.has_payment_id;
5867  num_subaddresses += info.is_subaddress;
5868 
5869  if (info.has_payment_id || !payment_id_uri.empty())
5870  {
5871  if (payment_id_seen)
5872  {
5873  fail_msg_writer() << tr("a single transaction cannot use more than one payment id");
5874  return false;
5875  }
5876 
5877  crypto::hash payment_id;
5878  std::string extra_nonce;
5879  if (info.has_payment_id)
5880  {
5881  memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
5882  memset(payment_id.data + 8, 0, 24); // merely a sanity check
5883  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
5884  }
5885  else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id))
5886  {
5888  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
5889  }
5890  else
5891  {
5892  fail_msg_writer() << tr("failed to parse payment id, though it was detected");
5893  return false;
5894  }
5895  bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
5896  if(!r)
5897  {
5898  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
5899  return false;
5900  }
5901  payment_id_seen = true;
5902  }
5903 
5904  dsts.push_back(de);
5905  }
5906 
5907  // prompt is there is no payment id and confirmation is required
5908  if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
5909  {
5910  std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
5911  if (std::cin.eof())
5912  return false;
5913  if (!command_line::is_yes(accepted))
5914  {
5915  fail_msg_writer() << tr("transaction cancelled.");
5916 
5917  return false;
5918  }
5919  }
5920 
5921  SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
5922 
5923  try
5924  {
5925  // figure out what tx will be necessary
5926  std::vector<tools::wallet2::pending_tx> ptx_vector;
5927  uint64_t bc_height, unlock_block = 0;
5928  std::string err;
5929  switch (transfer_type)
5930  {
5931  case TransferLocked:
5932  bc_height = get_daemon_blockchain_height(err);
5933  if (!err.empty())
5934  {
5935  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
5936  return false;
5937  }
5938  unlock_block = bc_height + locked_blocks;
5939  ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
5940  break;
5941  default:
5942  LOG_ERROR("Unknown transfer method, using default");
5943  /* FALLTHRU */
5944  case Transfer:
5945  ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
5946  break;
5947  }
5948 
5949  if (ptx_vector.empty())
5950  {
5951  fail_msg_writer() << tr("No outputs found, or daemon is not ready");
5952  return false;
5953  }
5954 
5955  // if we need to check for backlog, check the worst case tx
5956  if (m_wallet->confirm_backlog())
5957  {
5958  std::stringstream prompt;
5959  double worst_fee_per_byte = std::numeric_limits<double>::max();
5960  for (size_t n = 0; n < ptx_vector.size(); ++n)
5961  {
5962  const uint64_t blob_size = cryptonote::tx_to_blob(ptx_vector[n].tx).size();
5963  const double fee_per_byte = ptx_vector[n].fee / (double)blob_size;
5964  if (fee_per_byte < worst_fee_per_byte)
5965  {
5966  worst_fee_per_byte = fee_per_byte;
5967  }
5968  }
5969  try
5970  {
5971  std::vector<std::pair<uint64_t, uint64_t>> nblocks = m_wallet->estimate_backlog({std::make_pair(worst_fee_per_byte, worst_fee_per_byte)});
5972  if (nblocks.size() != 1)
5973  {
5974  prompt << "Internal error checking for backlog. " << tr("Is this okay anyway?");
5975  }
5976  else
5977  {
5978  if (nblocks[0].first > m_wallet->get_confirm_backlog_threshold())
5979  prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay?")) % nblocks[0].first).str();
5980  }
5981  }
5982  catch (const std::exception &e)
5983  {
5984  prompt << tr("Failed to check for backlog: ") << e.what() << ENDL << tr("Is this okay anyway?");
5985  }
5986 
5987  std::string prompt_str = prompt.str();
5988  if (!prompt_str.empty())
5989  {
5990  std::string accepted = input_line(prompt_str, true);
5991  if (std::cin.eof())
5992  return false;
5993  if (!command_line::is_yes(accepted))
5994  {
5995  fail_msg_writer() << tr("transaction cancelled.");
5996 
5997  return false;
5998  }
5999  }
6000  }
6001 
6002  // if more than one tx necessary, prompt user to confirm
6003  if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
6004  {
6005  uint64_t total_sent = 0;
6006  uint64_t total_fee = 0;
6007  uint64_t dust_not_in_fee = 0;
6008  uint64_t dust_in_fee = 0;
6009  uint64_t change = 0;
6010  for (size_t n = 0; n < ptx_vector.size(); ++n)
6011  {
6012  total_fee += ptx_vector[n].fee;
6013  for (auto i: ptx_vector[n].selected_transfers)
6014  total_sent += m_wallet->get_transfer_details(i).amount();
6015  total_sent -= ptx_vector[n].change_dts.amount + ptx_vector[n].fee;
6016  change += ptx_vector[n].change_dts.amount;
6017 
6018  if (ptx_vector[n].dust_added_to_fee)
6019  dust_in_fee += ptx_vector[n].dust;
6020  else
6021  dust_not_in_fee += ptx_vector[n].dust;
6022  }
6023 
6024  std::stringstream prompt;
6025  for (size_t n = 0; n < ptx_vector.size(); ++n)
6026  {
6027  prompt << tr("\nTransaction ") << (n + 1) << "/" << ptx_vector.size() << ":\n";
6028  subaddr_indices.clear();
6029  for (uint32_t i : ptx_vector[n].construction_data.subaddr_indices)
6030  subaddr_indices.insert(i);
6031  for (uint32_t i : subaddr_indices)
6032  prompt << boost::format(tr("Spending from address index %d\n")) % i;
6033  if (subaddr_indices.size() > 1)
6034  prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
6035  }
6036  prompt << boost::format(tr("Sending %s. ")) % print_etn(total_sent);
6037  if (ptx_vector.size() > 1)
6038  {
6039  prompt << boost::format(tr("Your transaction needs to be split into %llu transactions. "
6040  "This will result in a transaction fee being applied to each transaction, for a total fee of %s")) %
6041  ((unsigned long long)ptx_vector.size()) % print_etn(total_fee);
6042  }
6043  else
6044  {
6045  prompt << boost::format(tr("The transaction fee is %s")) %
6046  print_etn(total_fee);
6047  }
6048  if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_etn(dust_in_fee);
6049  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"))
6050  % print_etn(dust_not_in_fee);
6051  if (transfer_type == TransferLocked)
6052  {
6053  float days = locked_blocks / 720.0f;
6054  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;
6055  }
6056  if (m_wallet->print_ring_members())
6057  {
6058  if (!print_ring_members(ptx_vector, prompt))
6059  return false;
6060  }
6061  bool default_ring_size = true;
6062  for (const auto &ptx: ptx_vector)
6063  {
6064  for (const auto &vin: ptx.tx.vin)
6065  {
6066  if (vin.type() == typeid(txin_to_key))
6067  {
6068  const txin_to_key& in_to_key = boost::get<txin_to_key>(vin);
6069  if (in_to_key.key_offsets.size() != DEFAULT_MIX + 1)
6070  default_ring_size = false;
6071  }
6072  }
6073  }
6074  if (m_wallet->confirm_non_default_ring_size() && !default_ring_size)
6075  {
6076  prompt << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
6077  }
6078  prompt << ENDL << tr("Is this okay?");
6079 
6080  std::string accepted = input_line(prompt.str(), true);
6081  if (std::cin.eof())
6082  return false;
6083  if (!command_line::is_yes(accepted))
6084  {
6085  fail_msg_writer() << tr("transaction cancelled.");
6086 
6087  return false;
6088  }
6089 
6090  }
6091 
6092  // actually commit the transactions
6093  if (m_wallet->multisig() && called_by_mms)
6094  {
6095  std::string ciphertext = m_wallet->save_multisig_tx(ptx_vector);
6096  if (!ciphertext.empty())
6097  {
6098  get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::partially_signed_tx, ciphertext);
6099  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to MMS");
6100  }
6101  }
6102  else if (m_wallet->multisig())
6103  {
6104  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6105  if (!r)
6106  {
6107  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6108  return false;
6109  }
6110  else
6111  {
6112  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6113  }
6114  }
6115  else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6116  {
6117  try
6118  {
6120  if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
6121  fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
6122  return false;
6123  }
6124 
6125  commit_or_save(signed_tx.ptx, m_do_not_relay);
6126  }
6127  catch (const std::exception& e)
6128  {
6129  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6130  return false;
6131  }
6132  catch (...)
6133  {
6134  LOG_ERROR("Unknown error");
6135  fail_msg_writer() << tr("unknown error");
6136  return false;
6137  }
6138  }
6139  else if (m_wallet->watch_only())
6140  {
6141  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6142  if (!r)
6143  {
6144  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6145  return false;
6146  }
6147  else
6148  {
6149  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6150  }
6151  }
6152  else
6153  {
6154  commit_or_save(ptx_vector, m_do_not_relay);
6155  }
6156  }
6157  catch (const std::exception &e)
6158  {
6159  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6160  return false;
6161  }
6162  catch (...)
6163  {
6164  LOG_ERROR("unknown error");
6165  fail_msg_writer() << tr("unknown error");
6166  return false;
6167  }
6168 
6169  return true;
6170 }
6171 //----------------------------------------------------------------------------------------------------
6172 bool simple_wallet::transfer(const std::vector<std::string> &args_)
6173 {
6174  transfer_main(Transfer, args_, false);
6175  return true;
6176 }
6177 //----------------------------------------------------------------------------------------------------
6178 bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
6179 {
6180  transfer_main(TransferLocked, args_, false);
6181  return true;
6182 }
6183 //----------------------------------------------------------------------------------------------------
6184 bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
6185 {
6186  return sweep_main(0, true, args_);
6187 }
6188 //----------------------------------------------------------------------------------------------------
6189 
6190 bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
6191 {
6192  if (!try_connect_to_daemon())
6193  return true;
6194 
6196 
6197  try
6198  {
6199  // figure out what tx will be necessary
6200  auto ptx_vector = m_wallet->create_unmixable_sweep_transactions();
6201 
6202  if (ptx_vector.empty())
6203  {
6204  fail_msg_writer() << tr("No unmixable outputs found");
6205  return true;
6206  }
6207 
6208  // give user total and fee, and prompt to confirm
6209  uint64_t total_fee = 0, total_unmixable = 0;
6210  for (size_t n = 0; n < ptx_vector.size(); ++n)
6211  {
6212  total_fee += ptx_vector[n].fee;
6213  for (auto i: ptx_vector[n].selected_transfers)
6214  total_unmixable += m_wallet->get_transfer_details(i).amount();
6215  }
6216 
6217  std::string prompt_str = tr("Sweeping ") + print_etn(total_unmixable);
6218  if (ptx_vector.size() > 1) {
6219  prompt_str = (boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6220  print_etn(total_unmixable) %
6221  ((unsigned long long)ptx_vector.size()) %
6222  print_etn(total_fee)).str();
6223  }
6224  else {
6225  prompt_str = (boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
6226  print_etn(total_unmixable) %
6227  print_etn(total_fee)).str();
6228  }
6229  std::string accepted = input_line(prompt_str, true);
6230  if (std::cin.eof())
6231  return true;
6232  if (!command_line::is_yes(accepted))
6233  {
6234  fail_msg_writer() << tr("transaction cancelled.");
6235 
6236  return true;
6237  }
6238 
6239  // actually commit the transactions
6240  if (m_wallet->multisig())
6241  {
6242  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6243  if (!r)
6244  {
6245  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6246  }
6247  else
6248  {
6249  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6250  }
6251  }
6252  else if (m_wallet->watch_only())
6253  {
6254  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6255  if (!r)
6256  {
6257  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6258  }
6259  else
6260  {
6261  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6262  }
6263  }
6264  else
6265  {
6266  commit_or_save(ptx_vector, m_do_not_relay);
6267  }
6268  }
6269  catch (const tools::error::not_enough_unlocked_etn& e)
6270  {
6271  fail_msg_writer() << tr("Not enough ETN in unlocked balance");
6272  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);
6273  if (std::cin.eof())
6274  return true;
6275  if (command_line::is_yes(accepted))
6276  {
6277  try
6278  {
6279  m_wallet->discard_unmixable_outputs();
6280  } catch (...) {}
6281  }
6282  }
6283  catch (const std::exception &e)
6284  {
6285  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6286  }
6287  catch (...)
6288  {
6289  LOG_ERROR("unknown error");
6290  fail_msg_writer() << tr("unknown error");
6291  }
6292 
6293  return true;
6294 }
6295 //----------------------------------------------------------------------------------------------------
6296 bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<std::string> &args_)
6297 {
6298  auto print_usage = [below]()
6299  {
6300  if (below)
6301  {
6302  PRINT_USAGE(USAGE_SWEEP_BELOW);
6303  }
6304  else
6305  {
6306  PRINT_USAGE(USAGE_SWEEP_ALL);
6307  }
6308  };
6309  if (args_.size() == 0)
6310  {
6311  fail_msg_writer() << tr("No address given");
6312  print_usage();
6313  return true;
6314  }
6315 
6316  if (!try_connect_to_daemon())
6317  return true;
6318 
6319  std::vector<std::string> local_args = args_;
6320 
6321  std::set<uint32_t> subaddr_indices;
6322  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
6323  {
6324  if (local_args[0] == "index=all")
6325  {
6326  for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i)
6327  subaddr_indices.insert(i);
6328  }
6329  else if (!parse_subaddress_indices(local_args[0], subaddr_indices))
6330  {
6331  print_usage();
6332  return true;
6333  }
6334  local_args.erase(local_args.begin());
6335  }
6336 
6337  uint32_t priority = 0;
6338  if (local_args.size() > 0 && parse_priority(local_args[0], priority))
6339  local_args.erase(local_args.begin());
6340 
6341  priority = m_wallet->adjust_priority(priority);
6342 
6343  size_t fake_outs_count = 0;
6344  if(local_args.size() > 0) {
6345  size_t ring_size;
6346  if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
6347  {
6348  fake_outs_count = m_wallet->default_mixin();
6349  if (fake_outs_count == 0)
6350  fake_outs_count = DEFAULT_MIX;
6351  }
6352  else if (ring_size == 0)
6353  {
6354  fail_msg_writer() << tr("Ring size must not be 0");
6355  return true;
6356  }
6357  else
6358  {
6359  fake_outs_count = ring_size - 1;
6360  local_args.erase(local_args.begin());
6361  }
6362  }
6363 
6364  uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6365  if (adjusted_fake_outs_count > fake_outs_count)
6366  {
6367  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();
6368  return true;
6369  }
6370  if (adjusted_fake_outs_count < fake_outs_count)
6371  {
6372  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();
6373  return true;
6374  }
6375 
6376  uint64_t unlock_block = 0;
6377  if (locked) {
6378  uint64_t locked_blocks = 0;
6379 
6380  if (local_args.size() < 2) {
6381  fail_msg_writer() << tr("missing lockedblocks parameter");
6382  return true;
6383  }
6384 
6385  try
6386  {
6387  locked_blocks = boost::lexical_cast<uint64_t>(local_args[1]);
6388  }
6389  catch (const std::exception &e)
6390  {
6391  fail_msg_writer() << tr("bad locked_blocks parameter");
6392  return true;
6393  }
6394  if (locked_blocks > 1000000)
6395  {
6396  fail_msg_writer() << tr("Locked blocks too high, max 1000000 (Ëœ4 yrs)");
6397  return true;
6398  }
6399  std::string err;
6400  uint64_t bc_height = get_daemon_blockchain_height(err);
6401  if (!err.empty())
6402  {
6403  fail_msg_writer() << tr("failed to get blockchain height: ") << err;
6404  return true;
6405  }
6406  unlock_block = bc_height + locked_blocks;
6407 
6408  local_args.erase(local_args.begin() + 1);
6409  }
6410 
6411  size_t outputs = 1;
6412  if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=")
6413  {
6414  if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8)))
6415  {
6416  fail_msg_writer() << tr("Failed to parse number of outputs");
6417  return true;
6418  }
6419  else if (outputs < 1)
6420  {
6421  fail_msg_writer() << tr("Amount of outputs should be greater than 0");
6422  return true;
6423  }
6424  else
6425  {
6426  local_args.erase(local_args.begin());
6427  }
6428  }
6429 
6430  std::vector<uint8_t> extra;
6431  bool payment_id_seen = false;
6432  if (local_args.size() >= 2)
6433  {
6434  std::string payment_id_str = local_args.back();
6435 
6436  crypto::hash payment_id;
6437  bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
6438  if(r)
6439  {
6441 
6442  std::string extra_nonce;
6443  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6444  r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
6445  payment_id_seen = true;
6446  }
6447 
6448  if(!r && local_args.size() == 3)
6449  {
6450  fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str;
6451  print_usage();
6452  return true;
6453  }
6454  if (payment_id_seen)
6455  local_args.pop_back();
6456  }
6457 
6459  if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[0], oa_prompter))
6460  {
6461  fail_msg_writer() << tr("failed to parse address");
6462  print_usage();
6463  return true;
6464  }
6465 
6466  if (info.has_payment_id)
6467  {
6468  if (payment_id_seen)
6469  {
6470  fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[0];
6471  return true;
6472  }
6473 
6474  std::string extra_nonce;
6475  crypto::hash payment_id = crypto::null_hash;
6476  memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
6477  memset(payment_id.data + 8, 0, 24); // merely a sanity check
6478 
6479  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6480  bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
6481  if(!r)
6482  {
6483  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
6484  return true;
6485  }
6486  payment_id_seen = true;
6487  }
6488 
6489  // prompt is there is no payment id and confirmation is required
6490  if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
6491  {
6492  std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
6493  if (std::cin.eof())
6494  return true;
6495  if (!command_line::is_yes(accepted))
6496  {
6497  fail_msg_writer() << tr("transaction cancelled.");
6498 
6499  return true;
6500  }
6501  }
6502 
6504 
6505  try
6506  {
6507  // figure out what tx will be necessary
6508  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);
6509 
6510  if (ptx_vector.empty())
6511  {
6512  fail_msg_writer() << tr("No outputs found, or daemon is not ready");
6513  return true;
6514  }
6515 
6516  // give user total and fee, and prompt to confirm
6517  uint64_t total_fee = 0, total_sent = 0;
6518  for (size_t n = 0; n < ptx_vector.size(); ++n)
6519  {
6520  total_fee += ptx_vector[n].fee;
6521  for (auto i: ptx_vector[n].selected_transfers)
6522  total_sent += m_wallet->get_transfer_details(i).amount();
6523  }
6524 
6525  std::ostringstream prompt;
6526  for (size_t n = 0; n < ptx_vector.size(); ++n)
6527  {
6528  prompt << tr("\nTransaction ") << (n + 1) << "/" << ptx_vector.size() << ":\n";
6529  subaddr_indices.clear();
6530  for (uint32_t i : ptx_vector[n].construction_data.subaddr_indices)
6531  subaddr_indices.insert(i);
6532  for (uint32_t i : subaddr_indices)
6533  prompt << boost::format(tr("Spending from address index %d\n")) % i;
6534  if (subaddr_indices.size() > 1)
6535  prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
6536  }
6537  if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
6538  return true;
6539  if (ptx_vector.size() > 1) {
6540  prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6541  print_etn(total_sent) %
6542  ((unsigned long long)ptx_vector.size()) %
6543  print_etn(total_fee);
6544  }
6545  else {
6546  prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
6547  print_etn(total_sent) %
6548  print_etn(total_fee);
6549  }
6550  std::string accepted = input_line(prompt.str(), true);
6551  if (std::cin.eof())
6552  return true;
6553  if (!command_line::is_yes(accepted))
6554  {
6555  fail_msg_writer() << tr("transaction cancelled.");
6556 
6557  return true;
6558  }
6559 
6560  // actually commit the transactions
6561  if (m_wallet->multisig())
6562  {
6563  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6564  if (!r)
6565  {
6566  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6567  }
6568  else
6569  {
6570  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6571  }
6572  }
6573  else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6574  {
6575  try
6576  {
6578  std::vector<cryptonote::address_parse_info> dsts_info;
6579  dsts_info.push_back(info);
6580 
6581  if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
6582  fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
6583  return true;
6584  }
6585 
6586  commit_or_save(signed_tx.ptx, m_do_not_relay);
6587  }
6588  catch (const std::exception& e)
6589  {
6590  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6591  }
6592  catch (...)
6593  {
6594  LOG_ERROR("Unknown error");
6595  fail_msg_writer() << tr("unknown error");
6596  }
6597  }
6598  else if (m_wallet->watch_only())
6599  {
6600  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6601  if (!r)
6602  {
6603  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6604  }
6605  else
6606  {
6607  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6608  }
6609  }
6610  else
6611  {
6612  commit_or_save(ptx_vector, m_do_not_relay);
6613  }
6614  }
6615  catch (const std::exception& e)
6616  {
6617  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6618  }
6619  catch (...)
6620  {
6621  LOG_ERROR("unknown error");
6622  fail_msg_writer() << tr("unknown error");
6623  }
6624 
6625  return true;
6626 }
6627 //----------------------------------------------------------------------------------------------------
6628 bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
6629 {
6630  if (!try_connect_to_daemon())
6631  return true;
6632 
6633  std::vector<std::string> local_args = args_;
6634 
6635  uint32_t priority = 0;
6636  if (local_args.size() > 0 && parse_priority(local_args[0], priority))
6637  local_args.erase(local_args.begin());
6638 
6639  priority = m_wallet->adjust_priority(priority);
6640 
6641  size_t fake_outs_count = 0;
6642  if(local_args.size() > 0) {
6643  size_t ring_size;
6644  if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
6645  {
6646  fake_outs_count = m_wallet->default_mixin();
6647  if (fake_outs_count == 0)
6648  fake_outs_count = DEFAULT_MIX;
6649  }
6650  else if (ring_size == 0)
6651  {
6652  fail_msg_writer() << tr("Ring size must not be 0");
6653  return true;
6654  }
6655  else
6656  {
6657  fake_outs_count = ring_size - 1;
6658  local_args.erase(local_args.begin());
6659  }
6660  }
6661 
6662  uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6663  if (adjusted_fake_outs_count > fake_outs_count)
6664  {
6665  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();
6666  return true;
6667  }
6668  if (adjusted_fake_outs_count < fake_outs_count)
6669  {
6670  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();
6671  return true;
6672  }
6673 
6674  size_t outputs = 1;
6675  if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=")
6676  {
6677  if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8)))
6678  {
6679  fail_msg_writer() << tr("Failed to parse number of outputs");
6680  return true;
6681  }
6682  else if (outputs < 1)
6683  {
6684  fail_msg_writer() << tr("Amount of outputs should be greater than 0");
6685  return true;
6686  }
6687  else
6688  {
6689  local_args.erase(local_args.begin());
6690  }
6691  }
6692 
6693  std::vector<uint8_t> extra;
6694  bool payment_id_seen = false;
6695  if (local_args.size() == 3)
6696  {
6697  crypto::hash payment_id;
6698  crypto::hash8 payment_id8;
6699  std::string extra_nonce;
6700  if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id))
6701  {
6703  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6704  }
6705  else
6706  {
6707  fail_msg_writer() << tr("failed to parse Payment ID");
6708  return true;
6709  }
6710 
6711  if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
6712  {
6713  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
6714  return true;
6715  }
6716 
6717  local_args.pop_back();
6718  payment_id_seen = true;
6719  }
6720 
6721  if (local_args.size() != 2)
6722  {
6723  PRINT_USAGE(USAGE_SWEEP_SINGLE);
6724  return true;
6725  }
6726 
6727  crypto::key_image ki;
6728  if (!epee::string_tools::hex_to_pod(local_args[0], ki))
6729  {
6730  fail_msg_writer() << tr("failed to parse key image");
6731  return true;
6732  }
6733 
6735  if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[1], oa_prompter))
6736  {
6737  fail_msg_writer() << tr("failed to parse address");
6738  return true;
6739  }
6740 
6741  if (info.has_payment_id)
6742  {
6743  if (payment_id_seen)
6744  {
6745  fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[0];
6746  return true;
6747  }
6748 
6749  std::string extra_nonce;
6750  crypto::hash payment_id = crypto::null_hash;
6751  memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
6752  memset(payment_id.data + 8, 0, 24); // merely a sanity check
6753 
6754  set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
6755  if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
6756  {
6757  fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
6758  return true;
6759  }
6760  payment_id_seen = true;
6761  }
6762 
6763  // prompt if there is no payment id and confirmation is required
6764  if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
6765  {
6766  std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
6767  if (std::cin.eof())
6768  return true;
6769  if (!command_line::is_yes(accepted))
6770  {
6771  fail_msg_writer() << tr("transaction cancelled.");
6772 
6773  // would like to return false, because no tx made, but everything else returns true
6774  // and I don't know what returning false might adversely affect. *sigh*
6775  return true;
6776  }
6777  }
6778 
6780 
6781  try
6782  {
6783  // figure out what tx will be necessary
6784  auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra);
6785 
6786  if (ptx_vector.empty())
6787  {
6788  fail_msg_writer() << tr("No outputs found");
6789  return true;
6790  }
6791  if (ptx_vector.size() > 1)
6792  {
6793  fail_msg_writer() << tr("Multiple transactions are created, which is not supposed to happen");
6794  return true;
6795  }
6796  if (ptx_vector[0].selected_transfers.size() != 1)
6797  {
6798  fail_msg_writer() << tr("The transaction uses multiple or no inputs, which is not supposed to happen");
6799  return true;
6800  }
6801 
6802  // give user total and fee, and prompt to confirm
6803  uint64_t total_fee = ptx_vector[0].fee;
6804  uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount();
6805  std::ostringstream prompt;
6806  if (!print_ring_members(ptx_vector, prompt))
6807  return true;
6808  prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
6809  print_etn(total_sent) %
6810  print_etn(total_fee);
6811  std::string accepted = input_line(prompt.str(), true);
6812  if (std::cin.eof())
6813  return true;
6814  if (!command_line::is_yes(accepted))
6815  {
6816  fail_msg_writer() << tr("transaction cancelled.");
6817  return true;
6818  }
6819 
6820  // actually commit the transactions
6821  if (m_wallet->multisig())
6822  {
6823  bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_electroneum_tx");
6824  if (!r)
6825  {
6826  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6827  }
6828  else
6829  {
6830  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_electroneum_tx";
6831  }
6832  }
6833  else if (m_wallet->watch_only())
6834  {
6835  bool r = m_wallet->save_tx(ptx_vector, "unsigned_electroneum_tx");
6836  if (!r)
6837  {
6838  fail_msg_writer() << tr("Failed to write transaction(s) to file");
6839  }
6840  else
6841  {
6842  success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_electroneum_tx";
6843  }
6844  }
6845  else
6846  {
6847  m_wallet->commit_tx(ptx_vector[0]);
6848  success_msg_writer(true) << tr("ETN successfully sent, transaction: ") << get_transaction_hash(ptx_vector[0].tx);
6849  }
6850 
6851  }
6852  catch (const std::exception& e)
6853  {
6854  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6855  }
6856  catch (...)
6857  {
6858  LOG_ERROR("unknown error");
6859  fail_msg_writer() << tr("unknown error");
6860  }
6861 
6862  return true;
6863 }
6864 //----------------------------------------------------------------------------------------------------
6865 bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
6866 {
6867  return sweep_main(0, false, args_);
6868 }
6869 //----------------------------------------------------------------------------------------------------
6870 bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
6871 {
6872  uint64_t below = 0;
6873  if (args_.size() < 1)
6874  {
6875  fail_msg_writer() << tr("missing threshold amount");
6876  return true;
6877  }
6878  if (!cryptonote::parse_amount(below, args_[0]))
6879  {
6880  fail_msg_writer() << tr("invalid amount threshold");
6881  return true;
6882  }
6883  return sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end()));
6884 }
6885 //----------------------------------------------------------------------------------------------------
6886 bool simple_wallet::donate(const std::vector<std::string> &args_)
6887 {
6888  std::vector<std::string> local_args = args_;
6889  if(local_args.empty() || local_args.size() > 5)
6890  {
6891  PRINT_USAGE(USAGE_DONATE);
6892  return true;
6893  }
6894  std::string amount_str;
6895  std::string payment_id_str;
6896  // get payment id and pop
6897  crypto::hash payment_id;
6898  crypto::hash8 payment_id8;
6899  if (tools::wallet2::parse_long_payment_id (local_args.back(), payment_id ) ||
6900  tools::wallet2::parse_short_payment_id(local_args.back(), payment_id8))
6901  {
6902  payment_id_str = local_args.back();
6903  local_args.pop_back();
6904  }
6905  // get amount and pop
6906  uint64_t amount;
6907  bool ok = cryptonote::parse_amount(amount, local_args.back());
6908  if (ok && amount != 0)
6909  {
6910  amount_str = local_args.back();
6911  local_args.pop_back();
6912  }
6913  else
6914  {
6915  fail_msg_writer() << tr("amount is wrong: ") << local_args.back() << ", " << tr("expected number from 0 to ") << print_etn(std::numeric_limits<uint64_t>::max());
6916  return true;
6917  }
6918  // push back address, amount, payment id
6919  std::string address_str;
6920  if (m_wallet->nettype() != cryptonote::MAINNET)
6921  {
6922  // if not mainnet, convert donation address string to the relevant network type
6925  {
6926  fail_msg_writer() << tr("Failed to parse donation address: ") << ETN_DONATION_ADDR;
6927  return true;
6928  }
6929  address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
6930  }
6931  else
6932  {
6933  address_str = ETN_DONATION_ADDR;
6934  }
6935  local_args.push_back(address_str);
6936  local_args.push_back(amount_str);
6937  if (!payment_id_str.empty())
6938  local_args.push_back(payment_id_str);
6939  if (m_wallet->nettype() == cryptonote::MAINNET)
6940  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();
6941  else
6942  message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str();
6943  transfer(local_args);
6944  return true;
6945 }
6946 //----------------------------------------------------------------------------------------------------
6947 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)
6948 {
6949  // gather info to ask the user
6950  uint64_t amount = 0, amount_to_dests = 0, change = 0;
6951  size_t min_ring_size = ~0;
6952  std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
6953  int first_known_non_zero_change_index = -1;
6954  std::string payment_id_string = "";
6955  for (size_t n = 0; n < get_num_txes(); ++n)
6956  {
6957  const tools::wallet2::tx_construction_data &cd = get_tx(n);
6958 
6959  std::vector<tx_extra_field> tx_extra_fields;
6960  bool has_encrypted_payment_id = false;
6961  crypto::hash8 payment_id8 = crypto::null_hash8;
6962  if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
6963  {
6964  tx_extra_nonce extra_nonce;
6965  if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
6966  {
6967  crypto::hash payment_id;
6968  if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
6969  {
6970  if (!payment_id_string.empty())
6971  payment_id_string += ", ";
6972 
6973  // if none of the addresses are integrated addresses, it's a dummy one
6974  bool is_dummy = true;
6975  for (const auto &e: cd.dests)
6976  if (e.is_integrated)
6977  is_dummy = false;
6978 
6979  if (is_dummy)
6980  {
6981  payment_id_string += std::string("dummy payment ID");
6982  }
6983  else
6984  {
6985  crypto::hash payment_id = crypto::null_hash;
6986  memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
6987  memset(payment_id.data + 8, 0, 24); // merely a sanity check
6988  payment_id_string += std::string("payment ID ") + epee::string_tools::pod_to_hex(payment_id);
6989  }
6990  }
6991  else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
6992  {
6993  if (!payment_id_string.empty())
6994  payment_id_string += ", ";
6995  payment_id_string += std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id);
6996  payment_id_string += " (OBSOLETE)";
6997  }
6998  }
6999  }
7000 
7001  for (size_t s = 0; s < cd.sources.size(); ++s)
7002  {
7003  amount += cd.sources[s].amount;
7004  size_t ring_size = cd.sources[s].outputs.size();
7005  if (ring_size < min_ring_size)
7006  min_ring_size = ring_size;
7007  }
7008  for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
7009  {
7010  const tx_destination_entry &entry = cd.splitted_dsts[d];
7011  std::string address, standard_address = get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
7012  if (has_encrypted_payment_id && !entry.is_subaddress && standard_address != entry.original)
7013  {
7014  address = get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
7015  address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")");
7016  }
7017  else
7018  address = standard_address;
7019  auto i = dests.find(entry.addr);
7020  if (i == dests.end())
7021  dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
7022  else
7023  i->second.second += entry.amount;
7024  amount_to_dests += entry.amount;
7025  }
7026  if (cd.change_dts.amount > 0)
7027  {
7028  auto it = dests.find(cd.change_dts.addr);
7029  if (it == dests.end())
7030  {
7031  fail_msg_writer() << tr("Claimed change does not go to a paid address");
7032  return false;
7033  }
7034  if (it->second.second < cd.change_dts.amount)
7035  {
7036  fail_msg_writer() << tr("Claimed change is larger than payment to the change address");
7037  return false;
7038  }
7039  if (cd.change_dts.amount > 0)
7040  {
7041  if (first_known_non_zero_change_index == -1)
7042  first_known_non_zero_change_index = n;
7043  if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr)))
7044  {
7045  fail_msg_writer() << tr("Change goes to more than one address");
7046  return false;
7047  }
7048  }
7049  change += cd.change_dts.amount;
7050  it->second.second -= cd.change_dts.amount;
7051  if (it->second.second == 0)
7052  dests.erase(cd.change_dts.addr);
7053  }
7054  }
7055 
7056  if (payment_id_string.empty())
7057  payment_id_string = "no payment ID";
7058 
7059  std::string dest_string;
7060  size_t n_dummy_outputs = 0;
7061  for (auto i = dests.begin(); i != dests.end(); )
7062  {
7063  if (i->second.second > 0)
7064  {
7065  if (!dest_string.empty())
7066  dest_string += ", ";
7067  dest_string += (boost::format(tr("sending %s to %s")) % print_etn(i->second.second) % i->second.first).str();
7068  }
7069  else
7070  ++n_dummy_outputs;
7071  ++i;
7072  }
7073  if (n_dummy_outputs > 0)
7074  {
7075  if (!dest_string.empty())
7076  dest_string += ", ";
7077  dest_string += std::to_string(n_dummy_outputs) + tr(" dummy output(s)");
7078  }
7079  if (dest_string.empty())
7080  dest_string = tr("with no destinations");
7081 
7082  std::string change_string;
7083  if (change > 0)
7084  {
7085  std::string address = get_account_address_as_str(m_wallet->nettype(), get_tx(0).subaddr_account > 0, get_tx(0).change_dts.addr);
7086  change_string += (boost::format(tr("%s change to %s")) % print_etn(change) % address).str();
7087  }
7088  else
7089  change_string += tr("no change");
7090 
7091  uint64_t fee = amount - amount_to_dests;
7092  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();
7093  return command_line::is_yes(input_line(prompt_str, true));
7094 }
7095 //----------------------------------------------------------------------------------------------------
7096 bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
7097 {
7098  std::string extra_message;
7099  if (!txs.transfers.second.empty())
7100  extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str();
7101  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);
7102 }
7103 //----------------------------------------------------------------------------------------------------
7104 bool simple_wallet::accept_loaded_tx(const tools::wallet2::signed_tx_set &txs)
7105 {
7106  std::string extra_message;
7107  if (!txs.key_images.empty())
7108  extra_message = (boost::format("%u key images to import. ") % (unsigned)txs.key_images.size()).str();
7109  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);
7110 }
7111 //----------------------------------------------------------------------------------------------------
7112 bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
7113 {
7114  if (m_wallet->key_on_device())
7115  {
7116  fail_msg_writer() << tr("command not supported by HW wallet");
7117  return true;
7118  }
7119  if(m_wallet->multisig())
7120  {
7121  fail_msg_writer() << tr("This is a multisig wallet, it can only sign with sign_multisig");
7122  return true;
7123  }
7124  if(m_wallet->watch_only())
7125  {
7126  fail_msg_writer() << tr("This is a watch only wallet");
7127  return true;
7128  }
7129  if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export_raw"))
7130  {
7131  PRINT_USAGE(USAGE_SIGN_TRANSFER);
7132  return true;
7133  }
7134 
7136  const bool export_raw = args_.size() == 1;
7137 
7138  std::vector<tools::wallet2::pending_tx> ptx;
7139  try
7140  {
7141  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);
7142  if (!r)
7143  {
7144  fail_msg_writer() << tr("Failed to sign transaction");
7145  return true;
7146  }
7147  }
7148  catch (const std::exception &e)
7149  {
7150  fail_msg_writer() << tr("Failed to sign transaction: ") << e.what();
7151  return true;
7152  }
7153 
7154  std::string txids_as_text;
7155  for (const auto &t: ptx)
7156  {
7157  if (!txids_as_text.empty())
7158  txids_as_text += (", ");
7159  txids_as_text += epee::string_tools::pod_to_hex(get_transaction_hash(t.tx));
7160  }
7161  success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_electroneum_tx" << ", txid " << txids_as_text;
7162  if (export_raw)
7163  {
7164  std::string rawfiles_as_text;
7165  for (size_t i = 0; i < ptx.size(); ++i)
7166  {
7167  if (i > 0)
7168  rawfiles_as_text += ", ";
7169  rawfiles_as_text += "signed_electroneum_tx_raw" + (ptx.size() == 1 ? "" : ("_" + std::to_string(i)));
7170  }
7171  success_msg_writer(true) << tr("Transaction raw hex data exported to ") << rawfiles_as_text;
7172  }
7173  return true;
7174 }
7175 //----------------------------------------------------------------------------------------------------
7176 bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
7177 {
7178  if (m_wallet->key_on_device())
7179  {
7180  fail_msg_writer() << tr("command not supported by HW wallet");
7181  return true;
7182  }
7183  if (!try_connect_to_daemon())
7184  return true;
7185 
7186  try
7187  {
7188  std::vector<tools::wallet2::pending_tx> ptx_vector;
7189  bool r = m_wallet->load_tx("signed_electroneum_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); });
7190  if (!r)
7191  {
7192  fail_msg_writer() << tr("Failed to load transaction from file");
7193  return true;
7194  }
7195 
7196  commit_or_save(ptx_vector, false);
7197  }
7198  catch (const std::exception& e)
7199  {
7200  handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
7201  }
7202  catch (...)
7203  {
7204  LOG_ERROR("Unknown error");
7205  fail_msg_writer() << tr("unknown error");
7206  }
7207 
7208  return true;
7209 }
7210 //----------------------------------------------------------------------------------------------------
7211 bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
7212 {
7213  std::vector<std::string> local_args = args_;
7214 
7215  if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR)
7216  {
7217  fail_msg_writer() << tr("command not supported by HW wallet");
7218  return true;
7219  }
7220  if(local_args.size() != 1) {
7221  PRINT_USAGE(USAGE_GET_TX_KEY);
7222  return true;
7223  }
7224 
7225  crypto::hash txid;
7226  if (!epee::string_tools::hex_to_pod(local_args[0], txid))
7227  {
7228  fail_msg_writer() << tr("failed to parse txid");
7229  return true;
7230  }
7231 
7233 
7234  crypto::secret_key tx_key;
7235  std::vector<crypto::secret_key> additional_tx_keys;
7236 
7237  bool found_tx_key = m_wallet->get_tx_key(txid, tx_key, additional_tx_keys);
7238  if (found_tx_key)
7239  {
7240  ostringstream oss;
7241  oss << epee::string_tools::pod_to_hex(tx_key);
7242  for (size_t i = 0; i < additional_tx_keys.size(); ++i)
7243  oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
7244  success_msg_writer() << tr("Tx key: ") << oss.str();
7245  return true;
7246  }
7247  else
7248  {
7249  fail_msg_writer() << tr("no tx keys found for this txid");
7250  return true;
7251  }
7252 }
7253 //----------------------------------------------------------------------------------------------------
7254 bool simple_wallet::set_tx_key(const std::vector<std::string> &args_)
7255 {
7256  std::vector<std::string> local_args = args_;
7257 
7258  if(local_args.size() != 2) {
7259  PRINT_USAGE(USAGE_SET_TX_KEY);
7260  return true;
7261  }
7262 
7263  crypto::hash txid;
7264  if (!epee::string_tools::hex_to_pod(local_args[0], txid))
7265  {
7266  fail_msg_writer() << tr("failed to parse txid");
7267  return true;
7268  }
7269 
7270  crypto::secret_key tx_key;
7271  std::vector<crypto::secret_key> additional_tx_keys;
7272  try
7273  {
7274  if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key))
7275  {
7276  fail_msg_writer() << tr("failed to parse tx_key");
7277  return true;
7278  }
7279  while(true)
7280  {
7281  local_args[1] = local_args[1].substr(64);
7282  if (local_args[1].empty())
7283  break;
7284  additional_tx_keys.resize(additional_tx_keys.size() + 1);
7285  if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back()))
7286  {
7287  fail_msg_writer() << tr("failed to parse tx_key");
7288  return true;
7289  }
7290  }
7291  }
7292  catch (const std::out_of_range &e)
7293  {
7294  fail_msg_writer() << tr("failed to parse tx_key");
7295  return true;
7296  }
7297 
7298  LOCK_IDLE_SCOPE();
7299 
7300  try
7301  {
7302  m_wallet->set_tx_key(txid, tx_key, additional_tx_keys);
7303  success_msg_writer() << tr("Tx key successfully stored.");
7304  }
7305  catch (const std::exception &e)
7306  {
7307  fail_msg_writer() << tr("Failed to store tx key: ") << e.what();
7308  }
7309  return true;
7310 }
7311 //----------------------------------------------------------------------------------------------------
7312 bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
7313 {
7314  if (args.size() != 2 && args.size() != 3)
7315  {
7316  PRINT_USAGE(USAGE_GET_TX_PROOF);
7317  return true;
7318  }
7319 
7320  crypto::hash txid;
7321  if(!epee::string_tools::hex_to_pod(args[0], txid))
7322  {
7323  fail_msg_writer() << tr("failed to parse txid");
7324  return true;
7325  }
7326 
7328  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[1], oa_prompter))
7329  {
7330  fail_msg_writer() << tr("failed to parse address");
7331  return true;
7332  }
7333 
7335 
7336  try
7337  {
7338  std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : "");
7339  const std::string filename = "electroneum_tx_proof";
7340  if (epee::file_io_utils::save_string_to_file(filename, sig_str))
7341  success_msg_writer() << tr("signature file saved to: ") << filename;
7342  else
7343  fail_msg_writer() << tr("failed to save signature file");
7344  }
7345  catch (const std::exception &e)
7346  {
7347  fail_msg_writer() << tr("error: ") << e.what();
7348  }
7349  return true;
7350 }
7351 //----------------------------------------------------------------------------------------------------
7352 bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
7353 {
7354  std::vector<std::string> local_args = args_;
7355 
7356  if(local_args.size() != 3) {
7357  PRINT_USAGE(USAGE_CHECK_TX_KEY);
7358  return true;
7359  }
7360 
7361  if (!try_connect_to_daemon())
7362  return true;
7363 
7364  if (!m_wallet)
7365  {
7366  fail_msg_writer() << tr("wallet is null");
7367  return true;
7368  }
7369  crypto::hash txid;
7370  if(!epee::string_tools::hex_to_pod(local_args[0], txid))
7371  {
7372  fail_msg_writer() << tr("failed to parse txid");
7373  return true;
7374  }
7375 
7376  crypto::secret_key tx_key;
7377  std::vector<crypto::secret_key> additional_tx_keys;
7378  if(!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key))
7379  {
7380  fail_msg_writer() << tr("failed to parse tx key");
7381  return true;
7382  }
7383  local_args[1] = local_args[1].substr(64);
7384  while (!local_args[1].empty())
7385  {
7386  additional_tx_keys.resize(additional_tx_keys.size() + 1);
7387  if(!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back()))
7388  {
7389  fail_msg_writer() << tr("failed to parse tx key");
7390  return true;
7391  }
7392  local_args[1] = local_args[1].substr(64);
7393  }
7394 
7396  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[2], oa_prompter))
7397  {
7398  fail_msg_writer() << tr("failed to parse address");
7399  return true;
7400  }
7401 
7402  try
7403  {
7404  uint64_t received;
7405  bool in_pool;
7406  uint64_t confirmations;
7407  m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
7408 
7409  if (received > 0)
7410  {
7411  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;
7412  if (in_pool)
7413  {
7414  success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
7415  }
7416  else
7417  {
7418  if (confirmations != (uint64_t)-1)
7419  {
7420  success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
7421  }
7422  else
7423  {
7424  success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
7425  }
7426  }
7427  }
7428  else
7429  {
7430  fail_msg_writer() << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid;
7431  }
7432  }
7433  catch (const std::exception &e)
7434  {
7435  fail_msg_writer() << tr("error: ") << e.what();
7436  }
7437  return true;
7438 }
7439 //----------------------------------------------------------------------------------------------------
7440 bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
7441 {
7442  if(args.size() != 3 && args.size() != 4) {
7443  PRINT_USAGE(USAGE_CHECK_TX_PROOF);
7444  return true;
7445  }
7446 
7447  if (!try_connect_to_daemon())
7448  return true;
7449 
7450  // parse txid
7451  crypto::hash txid;
7452  if(!epee::string_tools::hex_to_pod(args[0], txid))
7453  {
7454  fail_msg_writer() << tr("failed to parse txid");
7455  return true;
7456  }
7457 
7458  // parse address
7460  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[1], oa_prompter))
7461  {
7462  fail_msg_writer() << tr("failed to parse address");
7463  return true;
7464  }
7465 
7466  // read signature file
7467  std::string sig_str;
7468  if (!epee::file_io_utils::load_file_to_string(args[2], sig_str))
7469  {
7470  fail_msg_writer() << tr("failed to load signature file");
7471  return true;
7472  }
7473 
7474  try
7475  {
7476  uint64_t received;
7477  bool in_pool;
7478  uint64_t confirmations;
7479  if (m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, args.size() == 4 ? args[3] : "", sig_str, received, in_pool, confirmations))
7480  {
7481  success_msg_writer() << tr("Good signature");
7482  if (received > 0)
7483  {
7484  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;
7485  if (in_pool)
7486  {
7487  success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
7488  }
7489  else
7490  {
7491  if (confirmations != (uint64_t)-1)
7492  {
7493  success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
7494  }
7495  else
7496  {
7497  success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
7498  }
7499  }
7500  }
7501  else
7502  {
7503  fail_msg_writer() << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid;
7504  }
7505  }
7506  else
7507  {
7508  fail_msg_writer() << tr("Bad signature");
7509  }
7510  }
7511  catch (const std::exception &e)
7512  {
7513  fail_msg_writer() << tr("error: ") << e.what();
7514  }
7515  return true;
7516 }
7517 //----------------------------------------------------------------------------------------------------
7518 bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
7519 {
7520  if (m_wallet->key_on_device())
7521  {
7522  fail_msg_writer() << tr("command not supported by HW wallet");
7523  return true;
7524  }
7525  if(args.size() != 1 && args.size() != 2) {
7526  PRINT_USAGE(USAGE_GET_SPEND_PROOF);
7527  return true;
7528  }
7529 
7530  if (m_wallet->watch_only())
7531  {
7532  fail_msg_writer() << tr("wallet is watch-only and cannot generate the proof");
7533  return true;
7534  }
7535 
7536  crypto::hash txid;
7537  if (!epee::string_tools::hex_to_pod(args[0], txid))
7538  {
7539  fail_msg_writer() << tr("failed to parse txid");
7540  return true;
7541  }
7542 
7543  if (!try_connect_to_daemon())
7544  return true;
7545 
7547 
7548  try
7549  {
7550  const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] : "");
7551  const std::string filename = "electroneum_spend_proof";
7552  if (epee::file_io_utils::save_string_to_file(filename, sig_str))
7553  success_msg_writer() << tr("signature file saved to: ") << filename;
7554  else
7555  fail_msg_writer() << tr("failed to save signature file");
7556  }
7557  catch (const std::exception &e)
7558  {
7559  fail_msg_writer() << e.what();
7560  }
7561  return true;
7562 }
7563 //----------------------------------------------------------------------------------------------------
7564 bool simple_wallet::check_spend_proof(const std::vector<std::string> &args)
7565 {
7566  if(args.size() != 2 && args.size() != 3) {
7567  PRINT_USAGE(USAGE_CHECK_SPEND_PROOF);
7568  return true;
7569  }
7570 
7571  crypto::hash txid;
7572  if (!epee::string_tools::hex_to_pod(args[0], txid))
7573  {
7574  fail_msg_writer() << tr("failed to parse txid");
7575  return true;
7576  }
7577 
7578  if (!try_connect_to_daemon())
7579  return true;
7580 
7581  std::string sig_str;
7582  if (!epee::file_io_utils::load_file_to_string(args[1], sig_str))
7583  {
7584  fail_msg_writer() << tr("failed to load signature file");
7585  return true;
7586  }
7587 
7588  try
7589  {
7590  if (m_wallet->check_spend_proof(txid, args.size() == 3 ? args[2] : "", sig_str))
7591  success_msg_writer() << tr("Good signature");
7592  else
7593  fail_msg_writer() << tr("Bad signature");
7594  }
7595  catch (const std::exception& e)
7596  {
7597  fail_msg_writer() << e.what();
7598  }
7599  return true;
7600 }
7601 //----------------------------------------------------------------------------------------------------
7602 bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args) //todo: reserve proofs including v10 outs (give them the chainstate indexes)
7603 {
7604  if (m_wallet->key_on_device())
7605  {
7606  fail_msg_writer() << tr("command not supported by HW wallet");
7607  return true;
7608  }
7609  if(args.size() != 1 && args.size() != 2) {
7610  PRINT_USAGE(USAGE_GET_RESERVE_PROOF);
7611  return true;
7612  }
7613 
7614  if (m_wallet->watch_only() || m_wallet->multisig())
7615  {
7616  fail_msg_writer() << tr("The reserve proof can be generated only by a full wallet");
7617  return true;
7618  }
7619 
7620  boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
7621  if (args[0] != "all")
7622  {
7623  account_minreserve = std::pair<uint32_t, uint64_t>();
7624  account_minreserve->first = m_current_subaddress_account;
7625  if (!cryptonote::parse_amount(account_minreserve->second, args[0]))
7626  {
7627  fail_msg_writer() << tr("amount is wrong: ") << args[0];
7628  return true;
7629  }
7630  }
7631 
7632  if (!try_connect_to_daemon())
7633  return true;
7634 
7636 
7637  try
7638  {
7639  const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] : "");
7640  const std::string filename = "electroneum_reserve_proof";
7641  if (epee::file_io_utils::save_string_to_file(filename, sig_str))
7642  success_msg_writer() << tr("signature file saved to: ") << filename;
7643  else
7644  fail_msg_writer() << tr("failed to save signature file");
7645  }
7646  catch (const std::exception &e)
7647  {
7648  fail_msg_writer() << e.what();
7649  }
7650  return true;
7651 }
7652 //----------------------------------------------------------------------------------------------------
7653 bool simple_wallet::check_reserve_proof(const std::vector<std::string> &args)
7654 {
7655  if(args.size() != 2 && args.size() != 3) {
7656  PRINT_USAGE(USAGE_CHECK_RESERVE_PROOF);
7657  return true;
7658  }
7659 
7660  if (!try_connect_to_daemon())
7661  return true;
7662 
7664  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[0], oa_prompter))
7665  {
7666  fail_msg_writer() << tr("failed to parse address");
7667  return true;
7668  }
7669  if (info.is_subaddress)
7670  {
7671  fail_msg_writer() << tr("Address must not be a subaddress");
7672  return true;
7673  }
7674 
7675  std::string sig_str;
7676  if (!epee::file_io_utils::load_file_to_string(args[1], sig_str))
7677  {
7678  fail_msg_writer() << tr("failed to load signature file");
7679  return true;
7680  }
7681 
7682  LOCK_IDLE_SCOPE();
7683 
7684  try
7685  {
7686  uint64_t total, spent;
7687  if (m_wallet->check_reserve_proof(info.address, args.size() == 3 ? args[2] : "", sig_str, total, spent))
7688  {
7689  success_msg_writer() << boost::format(tr("Good signature -- total: %s, spent: %s, unspent: %s")) % print_etn(total) % print_etn(spent) % print_etn(total - spent);
7690  }
7691  else
7692  {
7693  fail_msg_writer() << tr("Bad signature");
7694  }
7695  }
7696  catch (const std::exception& e)
7697  {
7698  fail_msg_writer() << e.what();
7699  }
7700  return true;
7701 }
7702 //----------------------------------------------------------------------------------------------------
7703 static std::string get_human_readable_timespan(std::chrono::seconds seconds)
7704 {
7705  uint64_t ts = seconds.count();
7706  if (ts < 60)
7707  return std::to_string(ts) + sw::tr(" seconds");
7708  if (ts < 3600)
7709  return std::to_string((uint64_t)(ts / 60)) + sw::tr(" minutes");
7710  if (ts < 3600 * 24)
7711  return std::to_string((uint64_t)(ts / 3600)) + sw::tr(" hours");
7712  if (ts < 3600 * 24 * 30.5)
7713  return std::to_string((uint64_t)(ts / (3600 * 24))) + sw::tr(" days");
7714  if (ts < 3600 * 24 * 365.25)
7715  return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + sw::tr(" months");
7716  return sw::tr("a long time");
7717 }
7718 //----------------------------------------------------------------------------------------------------
7719 // mutates local_args as it parses and consumes arguments
7720 bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vector<transfer_view>& transfers)
7721 {
7722  bool in = true;
7723  bool out = true;
7724  bool pending = true;
7725  bool failed = true;
7726  bool pool = true;
7727  bool coinbase = true;
7728  uint64_t min_height = 0;
7729  uint64_t max_height = (uint64_t)-1;
7730 
7731  // optional in/out selector
7732  if (local_args.size() > 0) {
7733  if (local_args[0] == "in" || local_args[0] == "incoming") {
7734  out = pending = failed = false;
7735  local_args.erase(local_args.begin());
7736  }
7737  else if (local_args[0] == "out" || local_args[0] == "outgoing") {
7738  in = pool = coinbase = false;
7739  local_args.erase(local_args.begin());
7740  }
7741  else if (local_args[0] == "pending") {
7742  in = out = failed = coinbase = false;
7743  local_args.erase(local_args.begin());
7744  }
7745  else if (local_args[0] == "failed") {
7746  in = out = pending = pool = coinbase = false;
7747  local_args.erase(local_args.begin());
7748  }
7749  else if (local_args[0] == "pool") {
7750  in = out = pending = failed = coinbase = false;
7751  local_args.erase(local_args.begin());
7752  }
7753  else if (local_args[0] == "coinbase") {
7754  in = out = pending = failed = pool = false;
7755  coinbase = true;
7756  local_args.erase(local_args.begin());
7757  }
7758  else if (local_args[0] == "all" || local_args[0] == "both") {
7759  local_args.erase(local_args.begin());
7760  }
7761  }
7762 
7763  // subaddr_index
7764  std::set<uint32_t> subaddr_indices;
7765  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
7766  {
7767  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
7768  return false;
7769  local_args.erase(local_args.begin());
7770  }
7771 
7772  // min height
7773  if (local_args.size() > 0 && local_args[0].find('=') == std::string::npos) {
7774  try {
7775  min_height = boost::lexical_cast<uint64_t>(local_args[0]);
7776  }
7777  catch (const boost::bad_lexical_cast &) {
7778  fail_msg_writer() << tr("bad min_height parameter:") << " " << local_args[0];
7779  return false;
7780  }
7781  local_args.erase(local_args.begin());
7782  }
7783 
7784  // max height
7785  if (local_args.size() > 0 && local_args[0].find('=') == std::string::npos) {
7786  try {
7787  max_height = boost::lexical_cast<uint64_t>(local_args[0]);
7788  }
7789  catch (const boost::bad_lexical_cast &) {
7790  fail_msg_writer() << tr("bad max_height parameter:") << " " << local_args[0];
7791  return false;
7792  }
7793  local_args.erase(local_args.begin());
7794  }
7795 
7796  const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
7797 
7798  if (in || coinbase) {
7799  std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
7800  m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7801  for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7802  const tools::wallet2::payment_details &pd = i->second;
7803  if (!pd.m_coinbase && !in)
7804  continue;
7805  std::string payment_id = string_tools::pod_to_hex(i->first);
7806  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7807  payment_id = payment_id.substr(0,16);
7808  std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
7809  std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
7810  const std::string type = pd.m_coinbase ? tr("block") : tr("in");
7811  const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
7812  std::string locked_msg = "unlocked";
7813  if (!unlocked)
7814  {
7815  locked_msg = "locked";
7816  const uint64_t unlock_time = pd.m_unlock_time;
7818  {
7819  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));
7820  if (bh >= last_block_height)
7821  locked_msg = std::to_string(bh - last_block_height) + " blks";
7822  }
7823  else
7824  {
7825  uint64_t current_time = static_cast<uint64_t>(time(NULL));
7827  if (threshold < pd.m_unlock_time)
7828  locked_msg = get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
7829  }
7830  }
7831  transfers.push_back({
7832  type,
7833  pd.m_block_height,
7834  pd.m_timestamp,
7835  type,
7836  true,
7837  pd.m_amount,
7838  pd.m_tx_hash,
7839  payment_id,
7840  0,
7841  {{destination, pd.m_amount}},
7842  {pd.m_subaddr_index.minor},
7843  note,
7844  locked_msg
7845  });
7846  }
7847  }
7848 
7849  if (out) {
7850  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
7851  m_wallet->get_payments_out(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7852  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7853  const tools::wallet2::confirmed_transfer_details &pd = i->second;
7854  uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
7855  uint64_t fee = pd.m_amount_in - pd.m_amount_out;
7856  std::vector<std::pair<std::string, uint64_t>> destinations;
7857  for (const auto &d: pd.m_dests) {
7858  destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
7859  }
7860  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
7861  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7862  payment_id = payment_id.substr(0,16);
7863  std::string note = m_wallet->get_tx_note(i->first);
7864  transfers.push_back({
7865  "out",
7866  pd.m_block_height,
7867  pd.m_timestamp,
7868  "out",
7869  true,
7870  pd.m_amount_in - change - fee,
7871  i->first,
7872  payment_id,
7873  fee,
7874  destinations,
7875  pd.m_subaddr_indices,
7876  note,
7877  "-"
7878  });
7879  }
7880  }
7881 
7882  if (pool) {
7883  try
7884  {
7885  m_in_manual_refresh.store(true, std::memory_order_relaxed);
7886  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);});
7887 
7888  m_wallet->update_pool_state();
7889  std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
7890  m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
7891  for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7892  const tools::wallet2::payment_details &pd = i->second.m_pd;
7893  std::string payment_id = string_tools::pod_to_hex(i->first);
7894  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7895  payment_id = payment_id.substr(0,16);
7896  std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
7897  std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
7898  std::string double_spend_note;
7899  if (i->second.m_double_spend_seen)
7900  double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] ");
7901  if (i->second.m_nonexistent_utxo_seen)
7902  success_msg_writer() << tr("Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
7903  transfers.push_back({
7904  "pool",
7905  "pool",
7906  pd.m_timestamp,
7907  "in",
7908  false,
7909  pd.m_amount,
7910  pd.m_tx_hash,
7911  payment_id,
7912  0,
7913  {{destination, pd.m_amount}},
7914  {pd.m_subaddr_index.minor},
7915  note + double_spend_note,
7916  "locked"
7917  });
7918  }
7919  }
7920  catch (const std::exception& e)
7921  {
7922  fail_msg_writer() << "Failed to get pool state:" << e.what();
7923  }
7924  }
7925 
7926  // print unconfirmed last
7927  if (pending || failed) {
7928  std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
7929  m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account, subaddr_indices);
7930  for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
7931  const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
7932  uint64_t amount = pd.m_amount_in;
7933  uint64_t fee = amount - pd.m_amount_out;
7934  std::vector<std::pair<std::string, uint64_t>> destinations;
7935  for (const auto &d: pd.m_dests) {
7936  destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
7937  }
7938  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
7939  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
7940  payment_id = payment_id.substr(0,16);
7941  std::string note = m_wallet->get_tx_note(i->first);
7943  if ((failed && is_failed) || (!is_failed && pending)) {
7944  transfers.push_back({
7945  (is_failed ? "failed" : "pending"),
7946  (is_failed ? "failed" : "pending"),
7947  pd.m_timestamp,
7948  "out",
7949  false,
7950  amount - pd.m_change - fee,
7951  i->first,
7952  payment_id,
7953  fee,
7954  destinations,
7955  pd.m_subaddr_indices,
7956  note,
7957  "-"
7958  });
7959  }
7960  }
7961  }
7962  // sort by block, then by timestamp (unconfirmed last)
7963  std::sort(transfers.begin(), transfers.end(), [](const transfer_view& a, const transfer_view& b) -> bool {
7964  if (a.confirmed && !b.confirmed)
7965  return true;
7966  if (a.block == b.block)
7967  return a.timestamp < b.timestamp;
7968  return a.block < b.block;
7969  });
7970 
7971  return true;
7972 }
7973 //----------------------------------------------------------------------------------------------------
7974 bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
7975 {
7976  std::vector<std::string> local_args = args_;
7977 
7978  if(local_args.size() > 4) {
7979  fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
7980  return true;
7981  }
7982 
7983  LOCK_IDLE_SCOPE();
7984 
7985  std::vector<transfer_view> all_transfers;
7986 
7987  if (!get_transfers(local_args, all_transfers))
7988  return true;
7989 
7990  PAUSE_READLINE();
7991 
7992  for (const auto& transfer : all_transfers)
7993  {
7994  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;
7995 
7996  std::string destinations = "-";
7997  if (!transfer.outputs.empty())
7998  {
7999  destinations = "";
8000  for (const auto& output : transfer.outputs)
8001  {
8002  if (!destinations.empty())
8003  destinations += ", ";
8004  destinations += (transfer.direction == "in" ? output.first.substr(0, 6) : output.first) + ":" + print_etn(output.second);
8005  }
8006  }
8007 
8008  auto formatter = boost::format("%8.8llu %6.6s %8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s");
8009 
8010  message_writer(color, false) << formatter
8011  % transfer.block
8012  % transfer.direction
8013  % transfer.unlocked
8014  % tools::get_human_readable_timestamp(transfer.timestamp)
8015  % print_etn(transfer.amount)
8016  % string_tools::pod_to_hex(transfer.hash)
8017  % transfer.payment_id
8018  % print_etn(transfer.fee)
8019  % destinations
8020  % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](uint32_t i) { return std::to_string(i); }), ", ")
8021  % transfer.note;
8022  }
8023 
8024  return true;
8025 }
8026 //----------------------------------------------------------------------------------------------------
8027 bool simple_wallet::export_transfers(const std::vector<std::string>& args_)
8028 {
8029  std::vector<std::string> local_args = args_;
8030 
8031  if(local_args.size() > 5) {
8032  fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]");
8033  return true;
8034  }
8035 
8036  LOCK_IDLE_SCOPE();
8037 
8038  std::vector<transfer_view> all_transfers;
8039 
8040  // might consumes arguments in local_args
8041  if (!get_transfers(local_args, all_transfers))
8042  return true;
8043 
8044  // output filename
8045  std::string filename = (boost::format("output%u.csv") % m_current_subaddress_account).str();
8046  if (local_args.size() > 0 && local_args[0].substr(0, 7) == "output=")
8047  {
8048  filename = local_args[0].substr(7, -1);
8049  local_args.erase(local_args.begin());
8050  }
8051 
8052  std::ofstream file(filename);
8053 
8054  // header
8055  file <<
8056  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") %
8057  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")
8058  << std::endl;
8059 
8060  uint64_t running_balance = 0;
8061  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");
8062 
8063  for (const auto& transfer : all_transfers)
8064  {
8065  // ignore unconfirmed transfers in running balance
8066  if (transfer.confirmed)
8067  {
8068  if (transfer.direction == "in" || transfer.direction == "block")
8069  running_balance += transfer.amount;
8070  else
8071  running_balance -= transfer.amount + transfer.fee;
8072  }
8073 
8074  file << formatter
8075  % transfer.block
8076  % transfer.direction
8077  % transfer.unlocked
8078  % tools::get_human_readable_timestamp(transfer.timestamp)
8079  % print_etn(transfer.amount)
8080  % print_etn(running_balance)
8081  % string_tools::pod_to_hex(transfer.hash)
8082  % transfer.payment_id
8083  % print_etn(transfer.fee)
8084  % (transfer.outputs.size() ? transfer.outputs[0].first : "-")
8085  % (transfer.outputs.size() ? print_etn(transfer.outputs[0].second) : "")
8086  % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](uint32_t i) { return std::to_string(i); }), ", ")
8087  % transfer.note
8088  << std::endl;
8089 
8090  for (size_t i = 1; i < transfer.outputs.size(); ++i)
8091  {
8092  file << formatter
8093  % ""
8094  % ""
8095  % ""
8096  % ""
8097  % ""
8098  % ""
8099  % ""
8100  % ""
8101  % ""
8102  % transfer.outputs[i].first
8103  % print_etn(transfer.outputs[i].second)
8104  % ""
8105  % ""
8106  << std::endl;
8107  }
8108  }
8109  file.close();
8110 
8111  success_msg_writer() << tr("CSV exported to ") << filename;
8112 
8113  return true;
8114 }
8115 //----------------------------------------------------------------------------------------------------
8116 bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
8117 {
8118  if(args_.size() > 3)
8119  {
8120  PRINT_USAGE(USAGE_UNSPENT_OUTPUTS);
8121  return true;
8122  }
8123  auto local_args = args_;
8124 
8125  std::set<uint32_t> subaddr_indices;
8126  if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
8127  {
8128  if (!parse_subaddress_indices(local_args[0], subaddr_indices))
8129  return true;
8130  local_args.erase(local_args.begin());
8131  }
8132 
8133  uint64_t min_amount = 0;
8134  uint64_t max_amount = std::numeric_limits<uint64_t>::max();
8135  if (local_args.size() > 0)
8136  {
8137  if (!cryptonote::parse_amount(min_amount, local_args[0]))
8138  {
8139  fail_msg_writer() << tr("amount is wrong: ") << local_args[0];
8140  return true;
8141  }
8142  local_args.erase(local_args.begin());
8143  if (local_args.size() > 0)
8144  {
8145  if (!cryptonote::parse_amount(max_amount, local_args[0]))
8146  {
8147  fail_msg_writer() << tr("amount is wrong: ") << local_args[0];
8148  return true;
8149  }
8150  local_args.erase(local_args.begin());
8151  }
8152  if (min_amount > max_amount)
8153  {
8154  fail_msg_writer() << tr("<min_amount> should be smaller than <max_amount>");
8155  return true;
8156  }
8157  }
8159  m_wallet->get_transfers(transfers);
8160  std::map<uint64_t, tools::wallet2::transfer_container> amount_to_tds;
8161  uint64_t min_height = std::numeric_limits<uint64_t>::max();
8162  uint64_t max_height = 0;
8163  uint64_t found_min_amount = std::numeric_limits<uint64_t>::max();
8164  uint64_t found_max_amount = 0;
8165  uint64_t count = 0;
8166  for (const auto& td : transfers)
8167  {
8168  uint64_t amount = td.amount();
8169  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()))
8170  continue;
8171  amount_to_tds[amount].push_back(td);
8172  if (min_height > td.m_block_height) min_height = td.m_block_height;
8173  if (max_height < td.m_block_height) max_height = td.m_block_height;
8174  if (found_min_amount > amount) found_min_amount = amount;
8175  if (found_max_amount < amount) found_max_amount = amount;
8176  ++count;
8177  }
8178  if (amount_to_tds.empty())
8179  {
8180  success_msg_writer() << tr("There is no unspent output in the specified address");
8181  return true;
8182  }
8183  for (const auto& amount_tds : amount_to_tds)
8184  {
8185  auto& tds = amount_tds.second;
8186  success_msg_writer() << tr("\nAmount: ") << print_etn(amount_tds.first) << tr(", number of keys: ") << tds.size();
8187  for (size_t i = 0; i < tds.size(); )
8188  {
8189  std::ostringstream oss;
8190  for (size_t j = 0; j < 8 && i < tds.size(); ++i, ++j)
8191  oss << tds[i].m_block_height << tr(" ");
8192  success_msg_writer() << oss.str();
8193  }
8194  }
8196  << tr("\nMin block height: ") << min_height
8197  << tr("\nMax block height: ") << max_height
8198  << tr("\nMin amount found: ") << print_etn(found_min_amount)
8199  << tr("\nMax amount found: ") << print_etn(found_max_amount)
8200  << tr("\nTotal count: ") << count;
8201  const size_t histogram_height = 10;
8202  const size_t histogram_width = 50;
8203  double bin_size = (max_height - min_height + 1.0) / histogram_width;
8204  size_t max_bin_count = 0;
8205  std::vector<size_t> histogram(histogram_width, 0);
8206  for (const auto& amount_tds : amount_to_tds)
8207  {
8208  for (auto& td : amount_tds.second)
8209  {
8210  uint64_t bin_index = (td.m_block_height - min_height + 1) / bin_size;
8211  if (bin_index >= histogram_width)
8212  bin_index = histogram_width - 1;
8213  histogram[bin_index]++;
8214  if (max_bin_count < histogram[bin_index])
8215  max_bin_count = histogram[bin_index];
8216  }
8217  }
8218  for (size_t x = 0; x < histogram_width; ++x)
8219  {
8220  double bin_count = histogram[x];
8221  if (max_bin_count > histogram_height)
8222  bin_count *= histogram_height / (double)max_bin_count;
8223  if (histogram[x] > 0 && bin_count < 1.0)
8224  bin_count = 1.0;
8225  histogram[x] = bin_count;
8226  }
8227  std::vector<std::string> histogram_line(histogram_height, std::string(histogram_width, ' '));
8228  for (size_t y = 0; y < histogram_height; ++y)
8229  {
8230  for (size_t x = 0; x < histogram_width; ++x)
8231  {
8232  if (y < histogram[x])
8233  histogram_line[y][x] = '*';
8234  }
8235  }
8236  double count_per_star = max_bin_count / (double)histogram_height;
8237  if (count_per_star < 1)
8238  count_per_star = 1;
8240  << tr("\nBin size: ") << bin_size
8241  << tr("\nOutputs per *: ") << count_per_star;
8242  ostringstream histogram_str;
8243  histogram_str << tr("count\n ^\n");
8244  for (size_t y = histogram_height; y > 0; --y)
8245  histogram_str << tr(" |") << histogram_line[y - 1] << tr("|\n");
8246  histogram_str
8247  << tr(" +") << std::string(histogram_width, '-') << tr("+--> block height\n")
8248  << tr(" ^") << std::string(histogram_width - 2, ' ') << tr("^\n")
8249  << tr(" ") << min_height << std::string(histogram_width - 8, ' ') << max_height;
8250  success_msg_writer() << histogram_str.str();
8251  return true;
8252 }
8253 //----------------------------------------------------------------------------------------------------
8254 bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
8255 {
8256  uint64_t start_height = 0;
8257  ResetType reset_type = ResetSoft;
8258 
8259  if (!args_.empty())
8260  {
8261  if (args_[0] == "hard")
8262  {
8263  reset_type = ResetHard;
8264  }
8265  else if (args_[0] == "soft")
8266  {
8267  reset_type = ResetSoft;
8268  }
8269  else if (args_[0] == "keep_ki")
8270  {
8271  reset_type = ResetSoftKeepKI;
8272  }
8273  else
8274  {
8275  PRINT_USAGE(USAGE_RESCAN_BC);
8276  return true;
8277  }
8278 
8279  if (args_.size() > 1)
8280  {
8281  try
8282  {
8283  start_height = boost::lexical_cast<uint64_t>( args_[1] );
8284  }
8285  catch(const boost::bad_lexical_cast &)
8286  {
8287  start_height = 0;
8288  }
8289  }
8290  }
8291 
8292  if (reset_type == ResetHard)
8293  {
8294  message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
8295  message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
8296  std::string confirm = input_line(tr("Rescan anyway?"), true);
8297  if(!std::cin.eof())
8298  {
8299  if (!command_line::is_yes(confirm))
8300  return true;
8301  }
8302  }
8303 
8304  const uint64_t wallet_from_height = m_wallet->get_refresh_from_block_height();
8305  if (start_height > wallet_from_height)
8306  {
8307  message_writer() << tr("Warning: your restore height is higher than wallet restore height: ") << wallet_from_height;
8308  std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
8309  if(!std::cin.eof())
8310  {
8311  if (!command_line::is_yes(confirm))
8312  return true;
8313  }
8314  }
8315 
8316  return refresh_main(start_height, reset_type, true);
8317 }
8318 //----------------------------------------------------------------------------------------------------
8319 void simple_wallet::check_for_messages()
8320 {
8321  try
8322  {
8323  std::vector<mms::message> new_messages;
8324  bool new_message = get_message_store().check_for_messages(get_multisig_wallet_state(), new_messages);
8325  if (new_message)
8326  {
8327  message_writer(console_color_magenta, true) << tr("MMS received new message");
8328  list_mms_messages(new_messages);
8329  m_cmd_binder.print_prompt();
8330  }
8331  }
8332  catch(...) {}
8333 }
8334 //----------------------------------------------------------------------------------------------------
8335 void simple_wallet::wallet_idle_thread()
8336 {
8337  while (true)
8338  {
8339  boost::unique_lock<boost::mutex> lock(m_idle_mutex);
8340  if (!m_idle_run.load(std::memory_order_relaxed))
8341  break;
8342 
8343  // auto refresh
8344  if (m_auto_refresh_enabled)
8345  {
8346  m_auto_refresh_refreshing = true;
8347  try
8348  {
8349  uint64_t fetched_blocks;
8350  bool received_etn;
8351  if (try_connect_to_daemon(true))
8352  m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, fetched_blocks, received_etn, false); // don't check the pool in background mode
8353  }
8354  catch(...) {}
8355  m_auto_refresh_refreshing = false;
8356  }
8357 
8358  // Check for new MMS messages;
8359  // For simplicity auto message check is ALSO controlled by "m_auto_refresh_enabled" and has no
8360  // separate thread either; thread syncing is tricky enough with only this one idle thread here
8361  if (m_auto_refresh_enabled && get_message_store().get_active())
8362  {
8363  check_for_messages();
8364  }
8365 
8366  if (!m_idle_run.load(std::memory_order_relaxed))
8367  break;
8368  m_idle_cond.wait_for(lock, boost::chrono::seconds(90));
8369  }
8370 }
8371 //----------------------------------------------------------------------------------------------------
8372 std::string simple_wallet::get_prompt() const
8373 {
8374  std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
8375  std::string prompt = std::string("[") + tr("wallet") + " " + addr_start;
8376  if (!m_wallet->check_connection(NULL))
8377  prompt += tr(" (no daemon)");
8378  else if (!m_wallet->is_synced())
8379  prompt += tr(" (out of sync)");
8380  prompt += "]: ";
8381  return prompt;
8382 }
8383 //----------------------------------------------------------------------------------------------------
8385 {
8386  // check and display warning, but go on anyway
8387  try_connect_to_daemon();
8388 
8389  //initial refresh
8390  refresh_main(0, ResetNone, true);
8391 
8392  m_auto_refresh_enabled = m_wallet->auto_refresh();
8393 
8394  // Idle thread which scans if m_auto_refresh_enabled, and listens to daemon
8395  m_idle_thread = boost::thread([&]{wallet_idle_thread();});
8396 
8397  message_writer(console_color_green, false) << "Background refresh thread started";
8398 
8399  //Indefinitely runs and listens on commands until user says to close wallet or an exception is thrown
8400  return m_cmd_binder.run_handling([this](){return get_prompt();}, "");
8401 }
8402 //----------------------------------------------------------------------------------------------------
8404 {
8405  m_cmd_binder.stop_handling();
8406 }
8407 //----------------------------------------------------------------------------------------------------
8408 bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8409 {
8410  // Usage:
8411  // account
8412  // account new <label text with white spaces allowed>
8413  // account switch <index>
8414  // account label <index> <label text with white spaces allowed>
8415  // account tag <tag_name> <account_index_1> [<account_index_2> ...]
8416  // account untag <account_index_1> [<account_index_2> ...]
8417  // account tag_description <tag_name> <description>
8418 
8419  if (args.empty())
8420  {
8421  // print all the existing accounts
8422  LOCK_IDLE_SCOPE();
8423  print_accounts();
8424  return true;
8425  }
8426 
8427  std::vector<std::string> local_args = args;
8428  std::string command = local_args[0];
8429  local_args.erase(local_args.begin());
8430  if (command == "new")
8431  {
8432  // create a new account and switch to it
8433  std::string label = boost::join(local_args, " ");
8434  if (label.empty())
8435  label = tr("(Untitled account)");
8436  m_wallet->add_subaddress_account(label);
8437  m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8438  // update_prompt();
8439  LOCK_IDLE_SCOPE();
8440  print_accounts();
8441  }
8442  else if(command == "generate" && local_args.size() == 1)
8443  {
8444  uint64_t count;
8445  if (!epee::string_tools::get_xtype_from_string(count, local_args[0]))
8446  {
8447  fail_msg_writer() << tr("failed to parse count: ") << local_args[0];
8448  return true;
8449  }
8450  if(count <= 0) {
8451  fail_msg_writer() << tr("specify a count higher than 0");
8452  return true;
8453  }
8454 
8455  try
8456  {
8457  uint64_t startAccountsCount = m_wallet->get_num_subaddress_accounts();
8458  // create N accounts
8459  for(uint64_t i = 0; i < count-1; i++) {
8460  m_wallet->add_subaddress_account("(Untitled account " + std::to_string(startAccountsCount + i+1) + " )", false);
8461 
8462  if(i % 1000 == 0) {
8463  success_msg_writer() << "Generating Accounts... (" << startAccountsCount + i+1 << "/" << startAccountsCount+count << ")";
8464  }
8465  }
8466  m_wallet->add_subaddress_account("(Untitled account " + std::to_string(startAccountsCount + count) + " )", true);
8467  success_msg_writer() << "Generating Accounts... (" << startAccountsCount + count << "/" << startAccountsCount+count << ")";
8468  m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8469  }
8470  catch (const std::exception& e)
8471  {
8472  fail_msg_writer() << e.what();
8473  }
8474 
8475  }
8476  else if (command == "switch" && local_args.size() == 1)
8477  {
8478  // switch to the specified account
8479  uint32_t index_major;
8480  if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0]))
8481  {
8482  fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
8483  return true;
8484  }
8485  if (index_major >= m_wallet->get_num_subaddress_accounts())
8486  {
8487  fail_msg_writer() << tr("specify an index between 0 and ") << (m_wallet->get_num_subaddress_accounts() - 1);
8488  return true;
8489  }
8490  m_current_subaddress_account = index_major;
8491  // update_prompt();
8492  show_balance();
8493  }
8494  else if (command == "label" && local_args.size() >= 1)
8495  {
8496  // set label of the specified account
8497  uint32_t index_major;
8498  if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0]))
8499  {
8500  fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
8501  return true;
8502  }
8503  local_args.erase(local_args.begin());
8504  std::string label = boost::join(local_args, " ");
8505  try
8506  {
8507  m_wallet->set_subaddress_label({index_major, 0}, label);
8508  LOCK_IDLE_SCOPE();
8509  print_accounts();
8510  }
8511  catch (const std::exception& e)
8512  {
8513  fail_msg_writer() << e.what();
8514  }
8515  }
8516  else if (command == "tag" && local_args.size() >= 2)
8517  {
8518  const std::string tag = local_args[0];
8519  std::set<uint32_t> account_indices;
8520  for (size_t i = 1; i < local_args.size(); ++i)
8521  {
8522  uint32_t account_index;
8523  if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
8524  {
8525  fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
8526  return true;
8527  }
8528  account_indices.insert(account_index);
8529  }
8530  try
8531  {
8532  m_wallet->set_account_tag(account_indices, tag);
8533  print_accounts(tag);
8534  }
8535  catch (const std::exception& e)
8536  {
8537  fail_msg_writer() << e.what();
8538  }
8539  }
8540  else if (command == "untag" && local_args.size() >= 1)
8541  {
8542  std::set<uint32_t> account_indices;
8543  for (size_t i = 0; i < local_args.size(); ++i)
8544  {
8545  uint32_t account_index;
8546  if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
8547  {
8548  fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
8549  return true;
8550  }
8551  account_indices.insert(account_index);
8552  }
8553  try
8554  {
8555  m_wallet->set_account_tag(account_indices, "");
8556  print_accounts();
8557  }
8558  catch (const std::exception& e)
8559  {
8560  fail_msg_writer() << e.what();
8561  }
8562  }
8563  else if (command == "tag_description" && local_args.size() >= 1)
8564  {
8565  const std::string tag = local_args[0];
8566  std::string description;
8567  if (local_args.size() > 1)
8568  {
8569  local_args.erase(local_args.begin());
8570  description = boost::join(local_args, " ");
8571  }
8572  try
8573  {
8574  m_wallet->set_account_tag_description(tag, description);
8575  print_accounts(tag);
8576  }
8577  catch (const std::exception& e)
8578  {
8579  fail_msg_writer() << e.what();
8580  }
8581  }
8582  else
8583  {
8584  PRINT_USAGE(USAGE_ACCOUNT);
8585  }
8586  return true;
8587 }
8588 //----------------------------------------------------------------------------------------------------
8589 void simple_wallet::print_accounts()
8590 {
8591  const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8592  size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
8593  for (const std::pair<std::string, std::string>& p : account_tags.first)
8594  {
8595  const std::string& tag = p.first;
8596  print_accounts(tag);
8597  num_untagged_accounts -= std::count(account_tags.second.begin(), account_tags.second.end(), tag);
8598  success_msg_writer() << "";
8599  }
8600 
8601  if (num_untagged_accounts > 0)
8602  print_accounts("");
8603 
8604  if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) {
8605  if (m_wallet->balance_all(false)) { //if they still have old outs, let them know
8606  success_msg_writer() << tr("\nGrand total:\n Pre V10 Balance: ") << print_etn(m_wallet->balance_all(false))
8607  << tr(", pre v10 unlocked balance: ")
8608  << print_etn(m_wallet->unlocked_balance_all(false));
8609  }
8610  success_msg_writer() << tr("\nGrand total:\n Balance: ") << print_etn(m_wallet->balance_all(true))
8611  << tr(", unlocked balance: ") << print_etn(m_wallet->unlocked_balance_all(true));
8612  }
8613  }
8614 //----------------------------------------------------------------------------------------------------
8615 void simple_wallet::print_accounts(const std::string& tag)
8616 {
8617  const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8618  if (tag.empty())
8619  {
8620  success_msg_writer() << tr("Untagged accounts:");
8621  }
8622  else
8623  {
8624  if (account_tags.first.count(tag) == 0)
8625  {
8626  fail_msg_writer() << boost::format(tr("Tag %s is unregistered.")) % tag;
8627  return;
8628  }
8629  success_msg_writer() << tr("Accounts with tag: ") << tag;
8630  success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
8631  }
8632 
8633  uint64_t total_balance = 0, total_unlocked_balance = 0, total_balance_public_chain = 0, total_unlocked_balance_public_chain = 0;
8634  bool found_old_bal = false;
8635  //quick check to see if there is *any* pre v10 balance. todo: Refactor another time for performance.
8636  for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8637  if (account_tags.second[account_index] != tag)
8638  continue;
8639  if (m_wallet->balance(account_index, false)) {
8640  found_old_bal = true;
8641  break;
8642  }
8643  }
8644 
8645  if (found_old_bal) {
8646  success_msg_writer() << "Pre V10 Account Balances";
8647  success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr(" Pre V10 Balance") %
8648  tr("Pre V10 Unlocked balance") % tr("Label");
8649  for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8650  if (account_tags.second[account_index] != tag)
8651  continue;
8652  success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
8653  % (m_current_subaddress_account == account_index ? '*' : ' ')
8654  % account_index
8655  % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8656  % print_etn(m_wallet->balance(account_index, false))
8657  % print_etn(m_wallet->unlocked_balance(account_index, false))
8658  % m_wallet->get_subaddress_label({account_index, 0});
8659  total_balance += m_wallet->balance(account_index, false);
8660  total_unlocked_balance += m_wallet->unlocked_balance(account_index, false);
8661  }
8663  << tr("----------------------------------------------------------------------------------");
8664  success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_etn(total_balance) %
8665  print_etn(total_unlocked_balance);
8666  success_msg_writer() << "";
8667  }
8668 
8669  //new balances
8670  if(m_wallet->synced_to_v10()) {
8671  success_msg_writer() << "Account Balances";
8672  success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") %
8673  tr("Unlocked balance") % tr("Label");
8674  for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8675  if (account_tags.second[account_index] != tag)
8676  continue;
8677  success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
8678  % (m_current_subaddress_account == account_index ? '*' : ' ')
8679  % account_index
8680  % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8681  % print_etn(m_wallet->balance(account_index, true))
8682  % print_etn(m_wallet->unlocked_balance(account_index, true))
8683  % m_wallet->get_subaddress_label({account_index, 0});
8684  total_balance_public_chain += m_wallet->balance(account_index, true);
8685  total_unlocked_balance_public_chain += m_wallet->unlocked_balance(account_index, true);
8686  }
8688  << tr("----------------------------------------------------------------------------------");
8689  success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_etn(total_balance_public_chain) %
8690  print_etn(total_unlocked_balance_public_chain);
8691  }
8692 
8693 }
8694 //----------------------------------------------------------------------------------------------------
8695 bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8696 {
8697  // Usage:
8698  // address
8699  // address new <label text with white spaces allowed>
8700  // address all
8701  // address <index_min> [<index_max>]
8702  // address label <index> <label text with white spaces allowed>
8703 
8704  std::vector<std::string> local_args = args;
8706  m_wallet->get_transfers(transfers);
8707 
8708  auto print_address_sub = [this, &transfers](uint32_t index)
8709  {
8710  bool used = std::find_if(
8711  transfers.begin(), transfers.end(),
8712  [this, &index](const tools::wallet2::transfer_details& td) {
8713  return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
8714  }) != transfers.end();
8715  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)") : "");
8716  };
8717 
8718  uint32_t index = 0;
8719  if (local_args.empty())
8720  {
8721  print_address_sub(index);
8722  }
8723  else if (local_args.size() == 1 && local_args[0] == "all")
8724  {
8725  local_args.erase(local_args.begin());
8726  for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
8727  print_address_sub(index);
8728  }
8729  else if (local_args[0] == "new")
8730  {
8731  local_args.erase(local_args.begin());
8732  std::string label;
8733  if (local_args.size() > 0)
8734  label = boost::join(local_args, " ");
8735  if (label.empty())
8736  label = tr("(Untitled address)");
8737  m_wallet->add_subaddress(m_current_subaddress_account, label);
8738  print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8739  }
8740  else if (local_args.size() >= 2 && local_args[0] == "label")
8741  {
8742  if (!epee::string_tools::get_xtype_from_string(index, local_args[1]))
8743  {
8744  fail_msg_writer() << tr("failed to parse index: ") << local_args[1];
8745  return true;
8746  }
8747  if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8748  {
8749  fail_msg_writer() << tr("specify an index between 0 and ") << (m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8750  return true;
8751  }
8752  local_args.erase(local_args.begin());
8753  local_args.erase(local_args.begin());
8754  std::string label = boost::join(local_args, " ");
8755  m_wallet->set_subaddress_label({m_current_subaddress_account, index}, label);
8756  print_address_sub(index);
8757  }
8758  else if (local_args.size() <= 2 && epee::string_tools::get_xtype_from_string(index, local_args[0]))
8759  {
8760  local_args.erase(local_args.begin());
8761  uint32_t index_min = index;
8762  uint32_t index_max = index_min;
8763  if (local_args.size() > 0)
8764  {
8765  if (!epee::string_tools::get_xtype_from_string(index_max, local_args[0]))
8766  {
8767  fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
8768  return true;
8769  }
8770  local_args.erase(local_args.begin());
8771  }
8772  if (index_max < index_min)
8773  std::swap(index_min, index_max);
8774  if (index_min >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8775  {
8776  fail_msg_writer() << tr("<index_min> is already out of bound");
8777  return true;
8778  }
8779  if (index_max >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8780  {
8781  message_writer() << tr("<index_max> exceeds the bound");
8782  index_max = m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1;
8783  }
8784  for (index = index_min; index <= index_max; ++index)
8785  print_address_sub(index);
8786  }
8787  else
8788  {
8789  PRINT_USAGE(USAGE_ADDRESS);
8790  }
8791 
8792  return true;
8793 }
8794 //----------------------------------------------------------------------------------------------------
8795 bool simple_wallet::print_integrated_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8796 {
8797  crypto::hash8 payment_id;
8798  if (args.size() > 1)
8799  {
8800  PRINT_USAGE(USAGE_INTEGRATED_ADDRESS);
8801  return true;
8802  }
8803  if (args.size() == 0)
8804  {
8805  if (m_current_subaddress_account != 0)
8806  {
8807  fail_msg_writer() << tr("Integrated addresses can only be created for account 0");
8808  return true;
8809  }
8810  payment_id = crypto::rand<crypto::hash8>();
8811  success_msg_writer() << tr("Random payment ID: ") << payment_id;
8812  success_msg_writer() << tr("Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8813  return true;
8814  }
8815  if(tools::wallet2::parse_short_payment_id(args.back(), payment_id))
8816  {
8817  if (m_current_subaddress_account != 0)
8818  {
8819  fail_msg_writer() << tr("Integrated addresses can only be created for account 0");
8820  return true;
8821  }
8822  success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8823  return true;
8824  }
8825  else {
8827  if(get_account_address_from_str(info, m_wallet->nettype(), args.back()))
8828  {
8829  if (info.has_payment_id)
8830  {
8831  success_msg_writer() << boost::format(tr("Integrated address: %s, payment ID: %s")) %
8832  get_account_address_as_str(m_wallet->nettype(), false, info.address) % epee::string_tools::pod_to_hex(info.payment_id);
8833  }
8834  else
8835  {
8836  success_msg_writer() << (info.is_subaddress ? tr("Subaddress: ") : tr("Standard address: ")) << get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
8837  }
8838  return true;
8839  }
8840  }
8841  fail_msg_writer() << tr("failed to parse payment ID or address");
8842  return true;
8843 }
8844 //----------------------------------------------------------------------------------------------------
8845 bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
8846 {
8847  if (args.size() == 0)
8848  {
8849  }
8850  else if (args.size() == 1 || (args[0] != "add" && args[0] != "delete"))
8851  {
8852  PRINT_USAGE(USAGE_ADDRESS_BOOK);
8853  return true;
8854  }
8855  else if (args[0] == "add")
8856  {
8858  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[1], oa_prompter))
8859  {
8860  fail_msg_writer() << tr("failed to parse address");
8861  return true;
8862  }
8863  crypto::hash payment_id = crypto::null_hash;
8864  size_t description_start = 2;
8865  if (info.has_payment_id)
8866  {
8867  memcpy(payment_id.data, info.payment_id.data, 8);
8868  }
8869  else if (!info.has_payment_id && args.size() >= 4 && args[2] == "pid")
8870  {
8871  if (tools::wallet2::parse_long_payment_id(args[3], payment_id))
8872  {
8874  description_start += 2;
8875  }
8876  else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id))
8877  {
8878  fail_msg_writer() << tr("Short payment IDs are to be used within an integrated address only");
8879  return true;
8880  }
8881  else
8882  {
8883  fail_msg_writer() << tr("failed to parse payment ID");
8884  return true;
8885  }
8886  }
8887  std::string description;
8888  for (size_t i = description_start; i < args.size(); ++i)
8889  {
8890  if (i > description_start)
8891  description += " ";
8892  description += args[i];
8893  }
8894  m_wallet->add_address_book_row(info.address, payment_id, description, info.is_subaddress);
8895  }
8896  else
8897  {
8898  size_t row_id;
8899  if(!epee::string_tools::get_xtype_from_string(row_id, args[1]))
8900  {
8901  fail_msg_writer() << tr("failed to parse index");
8902  return true;
8903  }
8904  m_wallet->delete_address_book_row(row_id);
8905  }
8906  auto address_book = m_wallet->get_address_book();
8907  if (address_book.empty())
8908  {
8909  success_msg_writer() << tr("Address book is empty.");
8910  }
8911  else
8912  {
8913  for (size_t i = 0; i < address_book.size(); ++i) {
8914  auto& row = address_book[i];
8915  success_msg_writer() << tr("Index: ") << i;
8916  success_msg_writer() << tr("Address: ") << get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
8917  success_msg_writer() << tr("Payment ID: ") << row.m_payment_id << " (OBSOLETE)";
8918  success_msg_writer() << tr("Description: ") << row.m_description << "\n";
8919  }
8920  }
8921  return true;
8922 }
8923 //----------------------------------------------------------------------------------------------------
8924 bool simple_wallet::set_tx_note(const std::vector<std::string> &args)
8925 {
8926  if (args.size() == 0)
8927  {
8928  PRINT_USAGE(USAGE_SET_TX_NOTE);
8929  return true;
8930  }
8931 
8932  cryptonote::blobdata txid_data;
8933  if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
8934  {
8935  fail_msg_writer() << tr("failed to parse txid");
8936  return true;
8937  }
8938  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
8939 
8940  std::string note = "";
8941  for (size_t n = 1; n < args.size(); ++n)
8942  {
8943  if (n > 1)
8944  note += " ";
8945  note += args[n];
8946  }
8947  m_wallet->set_tx_note(txid, note);
8948 
8949  return true;
8950 }
8951 //----------------------------------------------------------------------------------------------------
8952 bool simple_wallet::get_tx_note(const std::vector<std::string> &args)
8953 {
8954  if (args.size() != 1)
8955  {
8956  PRINT_USAGE(USAGE_GET_TX_NOTE);
8957  return true;
8958  }
8959 
8960  cryptonote::blobdata txid_data;
8961  if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
8962  {
8963  fail_msg_writer() << tr("failed to parse txid");
8964  return true;
8965  }
8966  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
8967 
8968  std::string note = m_wallet->get_tx_note(txid);
8969  if (note.empty())
8970  success_msg_writer() << "no note found";
8971  else
8972  success_msg_writer() << "note found: " << note;
8973 
8974  return true;
8975 }
8976 //----------------------------------------------------------------------------------------------------
8977 bool simple_wallet::set_description(const std::vector<std::string> &args)
8978 {
8979  // 0 arguments allowed, for setting the description to empty string
8980 
8981  std::string description = "";
8982  for (size_t n = 0; n < args.size(); ++n)
8983  {
8984  if (n > 0)
8985  description += " ";
8986  description += args[n];
8987  }
8988  m_wallet->set_description(description);
8989 
8990  return true;
8991 }
8992 //----------------------------------------------------------------------------------------------------
8993 bool simple_wallet::get_description(const std::vector<std::string> &args)
8994 {
8995  if (args.size() != 0)
8996  {
8997  PRINT_USAGE(USAGE_GET_DESCRIPTION);
8998  return true;
8999  }
9000 
9001  std::string description = m_wallet->get_description();
9002  if (description.empty())
9003  success_msg_writer() << tr("no description found");
9004  else
9005  success_msg_writer() << tr("description found: ") << description;
9006 
9007  return true;
9008 }
9009 //----------------------------------------------------------------------------------------------------
9010 bool simple_wallet::status(const std::vector<std::string> &args)
9011 {
9012  uint64_t local_height = m_wallet->get_blockchain_current_height();
9013  uint32_t version = 0;
9014  bool ssl = false;
9015  if (!m_wallet->check_connection(&version, &ssl))
9016  {
9017  success_msg_writer() << "Refreshed " << local_height << "/?, no daemon connected";
9018  return true;
9019  }
9020 
9021  std::string err;
9022  uint64_t bc_height = get_daemon_blockchain_height(err);
9023  if (err.empty())
9024  {
9025  bool synced = local_height == bc_height;
9026  success_msg_writer() << "Refreshed " << local_height << "/" << bc_height << ", " << (synced ? "synced" : "syncing")
9027  << ", daemon RPC v" << get_version_string(version) << ", " << (ssl ? "SSL" : "no SSL");
9028  }
9029  else
9030  {
9031  fail_msg_writer() << "Refreshed " << local_height << "/?, daemon connection error";
9032  }
9033  return true;
9034 }
9035 //----------------------------------------------------------------------------------------------------
9036 bool simple_wallet::wallet_info(const std::vector<std::string> &args)
9037 {
9038  bool ready;
9039  uint32_t threshold, total;
9040  std::string description = m_wallet->get_description();
9041  if (description.empty())
9042  {
9043  description = "<Not set>";
9044  }
9045  message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
9046  message_writer() << tr("Description: ") << description;
9047  message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
9048  std::string type;
9049  if (m_wallet->watch_only())
9050  type = tr("Watch only");
9051  else if (m_wallet->multisig(&ready, &threshold, &total))
9052  type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str();
9053  else
9054  type = tr("Normal");
9055  message_writer() << tr("Type: ") << type;
9056  message_writer() << tr("Network type: ") << (
9057  m_wallet->nettype() == cryptonote::TESTNET ? tr("Testnet") :
9058  m_wallet->nettype() == cryptonote::STAGENET ? tr("Stagenet") : tr("Mainnet"));
9059  return true;
9060 }
9061 //----------------------------------------------------------------------------------------------------
9062 bool simple_wallet::sign(const std::vector<std::string> &args)
9063 {
9064  if (m_wallet->key_on_device())
9065  {
9066  fail_msg_writer() << tr("command not supported by HW wallet");
9067  return true;
9068  }
9069  if (args.size() != 1)
9070  {
9071  PRINT_USAGE(USAGE_SIGN);
9072  return true;
9073  }
9074  if (m_wallet->watch_only())
9075  {
9076  fail_msg_writer() << tr("wallet is watch-only and cannot sign");
9077  return true;
9078  }
9079  if (m_wallet->multisig())
9080  {
9081  fail_msg_writer() << tr("This wallet is multisig and cannot sign");
9082  return true;
9083  }
9084 
9085  std::string filename = args[0];
9086  std::string data;
9087  bool r = epee::file_io_utils::load_file_to_string(filename, data);
9088  if (!r)
9089  {
9090  fail_msg_writer() << tr("failed to read file ") << filename;
9091  return true;
9092  }
9093 
9095 
9096  std::string signature = m_wallet->sign(data);
9098  return true;
9099 }
9100 //----------------------------------------------------------------------------------------------------
9101 bool simple_wallet::verify(const std::vector<std::string> &args)
9102 {
9103  if (args.size() != 3)
9104  {
9105  PRINT_USAGE(USAGE_VERIFY);
9106  return true;
9107  }
9108  std::string filename = args[0];
9109  std::string address_string = args[1];
9110  std::string signature= args[2];
9111 
9112  std::string data;
9113  bool r = epee::file_io_utils::load_file_to_string(filename, data);
9114  if (!r)
9115  {
9116  fail_msg_writer() << tr("failed to read file ") << filename;
9117  return true;
9118  }
9119 
9121  if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_string, oa_prompter))
9122  {
9123  fail_msg_writer() << tr("failed to parse address");
9124  return true;
9125  }
9126 
9127  r = m_wallet->verify(data, info.address, signature);
9128  if (!r)
9129  {
9130  fail_msg_writer() << tr("Bad signature from ") << address_string;
9131  }
9132  else
9133  {
9134  success_msg_writer() << tr("Good signature from ") << address_string;
9135  }
9136  return true;
9137 }
9138 //----------------------------------------------------------------------------------------------------
9139 bool simple_wallet::export_key_images(const std::vector<std::string> &args)
9140 {
9141  if (m_wallet->key_on_device())
9142  {
9143  fail_msg_writer() << tr("command not supported by HW wallet");
9144  return true;
9145  }
9146  if (args.size() != 1)
9147  {
9148  PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES);
9149  return true;
9150  }
9151  if (m_wallet->watch_only())
9152  {
9153  fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
9154  return true;
9155  }
9156 
9157  std::string filename = args[0];
9158  if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9159  return true;
9160 
9162 
9163  try
9164  {
9165  if (!m_wallet->export_key_images(filename))
9166  {
9167  fail_msg_writer() << tr("failed to save file ") << filename;
9168  return true;
9169  }
9170  }
9171  catch (const std::exception &e)
9172  {
9173  LOG_ERROR("Error exporting key images: " << e.what());
9174  fail_msg_writer() << "Error exporting key images: " << e.what();
9175  return true;
9176  }
9177 
9178  success_msg_writer() << tr("Signed key images exported to ") << filename;
9179  return true;
9180 }
9181 //----------------------------------------------------------------------------------------------------
9182 bool simple_wallet::import_key_images(const std::vector<std::string> &args)
9183 {
9184  if (m_wallet->key_on_device())
9185  {
9186  fail_msg_writer() << tr("command not supported by HW wallet");
9187  return true;
9188  }
9189  if (!m_wallet->is_trusted_daemon())
9190  {
9191  fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
9192  return true;
9193  }
9194 
9195  if (args.size() != 1)
9196  {
9197  PRINT_USAGE(USAGE_IMPORT_KEY_IMAGES);
9198  return true;
9199  }
9200  std::string filename = args[0];
9201 
9202  LOCK_IDLE_SCOPE();
9203  try
9204  {
9205  uint64_t spent = 0, unspent = 0;
9206  uint64_t height = m_wallet->import_key_images(filename, spent, unspent);
9207  success_msg_writer() << "Signed key images imported to height " << height << ", "
9208  << print_etn(spent) << " spent, " << print_etn(unspent) << " unspent";
9209  }
9210  catch (const std::exception &e)
9211  {
9212  fail_msg_writer() << "Failed to import key images: " << e.what();
9213  return true;
9214  }
9215 
9216  return true;
9217 }
9218 //----------------------------------------------------------------------------------------------------
9219 bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
9220 {
9221  if (!m_wallet->key_on_device())
9222  {
9223  fail_msg_writer() << tr("command only supported by HW wallet");
9224  return true;
9225  }
9226  if (!m_wallet->get_account().get_device().has_ki_cold_sync())
9227  {
9228  fail_msg_writer() << tr("hw wallet does not support cold KI sync");
9229  return true;
9230  }
9231 
9232  LOCK_IDLE_SCOPE();
9233  key_images_sync_intern();
9234  return true;
9235 }
9236 //----------------------------------------------------------------------------------------------------
9237 void simple_wallet::key_images_sync_intern(){
9238  try
9239  {
9240  message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
9241 
9242  uint64_t spent = 0, unspent = 0;
9243  uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
9244  if (height > 0)
9245  {
9246  success_msg_writer() << tr("Key images synchronized to height ") << height;
9247  if (!m_wallet->is_trusted_daemon())
9248  {
9249  message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
9250  } else
9251  {
9252  success_msg_writer() << print_etn(spent) << tr(" spent, ") << print_etn(unspent) << tr(" unspent");
9253  }
9254  }
9255  else {
9256  fail_msg_writer() << tr("Failed to import key images");
9257  }
9258  }
9259  catch (const std::exception &e)
9260  {
9261  fail_msg_writer() << tr("Failed to import key images: ") << e.what();
9262  }
9263 }
9264 //----------------------------------------------------------------------------------------------------
9265 bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
9266 {
9267  if (!m_wallet->key_on_device())
9268  {
9269  fail_msg_writer() << tr("command only supported by HW wallet");
9270  return true;
9271  }
9272 
9273  LOCK_IDLE_SCOPE();
9274  try
9275  {
9276  bool r = m_wallet->reconnect_device();
9277  if (!r){
9278  fail_msg_writer() << tr("Failed to reconnect device");
9279  }
9280  }
9281  catch (const std::exception &e)
9282  {
9283  fail_msg_writer() << tr("Failed to reconnect device: ") << tr(e.what());
9284  return true;
9285  }
9286 
9287  return true;
9288 }
9289 //----------------------------------------------------------------------------------------------------
9290 bool simple_wallet::export_outputs(const std::vector<std::string> &args)
9291 {
9292  if (m_wallet->key_on_device())
9293  {
9294  fail_msg_writer() << tr("command not supported by HW wallet");
9295  return true;
9296  }
9297  if (args.size() != 1)
9298  {
9299  PRINT_USAGE(USAGE_EXPORT_OUTPUTS);
9300  return true;
9301  }
9302 
9303  std::string filename = args[0];
9304  if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9305  return true;
9306 
9308 
9309  try
9310  {
9311  std::string data = m_wallet->export_outputs_to_str();
9312  bool r = epee::file_io_utils::save_string_to_file(filename, data);
9313  if (!r)
9314  {
9315  fail_msg_writer() << tr("failed to save file ") << filename;
9316  return true;
9317  }
9318  }
9319  catch (const std::exception &e)
9320  {
9321  LOG_ERROR("Error exporting outputs: " << e.what());
9322  fail_msg_writer() << "Error exporting outputs: " << e.what();
9323  return true;
9324  }
9325 
9326  success_msg_writer() << tr("Outputs exported to ") << filename;
9327  return true;
9328 }
9329 //----------------------------------------------------------------------------------------------------
9330 bool simple_wallet::import_outputs(const std::vector<std::string> &args)
9331 {
9332  if (m_wallet->key_on_device())
9333  {
9334  fail_msg_writer() << tr("command not supported by HW wallet");
9335  return true;
9336  }
9337  if (args.size() != 1)
9338  {
9339  PRINT_USAGE(USAGE_IMPORT_OUTPUTS);
9340  return true;
9341  }
9342  std::string filename = args[0];
9343 
9344  std::string data;
9345  bool r = epee::file_io_utils::load_file_to_string(filename, data);
9346  if (!r)
9347  {
9348  fail_msg_writer() << tr("failed to read file ") << filename;
9349  return true;
9350  }
9351 
9352  try
9353  {
9355  size_t n_outputs = m_wallet->import_outputs_from_str(data);
9356  success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
9357  }
9358  catch (const std::exception &e)
9359  {
9360  fail_msg_writer() << "Failed to import outputs " << filename << ": " << e.what();
9361  return true;
9362  }
9363 
9364  return true;
9365 }
9366 //----------------------------------------------------------------------------------------------------
9367 bool simple_wallet::show_transfer(const std::vector<std::string> &args)
9368 {
9369  if (args.size() != 1)
9370  {
9371  PRINT_USAGE(USAGE_SHOW_TRANSFER);
9372  return true;
9373  }
9374 
9375  cryptonote::blobdata txid_data;
9376  if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
9377  {
9378  fail_msg_writer() << tr("failed to parse txid");
9379  return true;
9380  }
9381  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
9382 
9383  const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
9384 
9385  std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
9386  m_wallet->get_payments(payments, 0, (uint64_t)-1, m_current_subaddress_account);
9387  for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
9388  const tools::wallet2::payment_details &pd = i->second;
9389  if (pd.m_tx_hash == txid) {
9390  std::string payment_id = string_tools::pod_to_hex(i->first);
9391  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9392  payment_id = payment_id.substr(0,16);
9393  success_msg_writer() << "Incoming transaction found";
9394  success_msg_writer() << "txid: " << txid;
9395  success_msg_writer() << "Height: " << pd.m_block_height;
9397  success_msg_writer() << "Amount: " << print_etn(pd.m_amount);
9398  success_msg_writer() << "Payment ID: " << payment_id;
9400  {
9401  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));
9402  uint64_t last_block_reward = m_wallet->get_last_block_reward();
9403  uint64_t suggested_threshold = last_block_reward ? (pd.m_amount + last_block_reward - 1) / last_block_reward : 0;
9404  if (bh >= last_block_height)
9405  success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock";
9406  else if (suggested_threshold > 0)
9407  success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations (" << suggested_threshold << " suggested threshold)";
9408  else
9409  success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations";
9410  }
9411  else
9412  {
9413  uint64_t current_time = static_cast<uint64_t>(time(NULL));
9415  if (threshold >= pd.m_unlock_time)
9416  success_msg_writer() << "unlocked for " << get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time));
9417  else
9418  success_msg_writer() << "locked for " << get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
9419  }
9420  success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
9421  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9422  return true;
9423  }
9424  }
9425 
9426  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
9427  m_wallet->get_payments_out(payments_out, 0, (uint64_t)-1, m_current_subaddress_account);
9428  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
9429  if (i->first == txid)
9430  {
9431  const tools::wallet2::confirmed_transfer_details &pd = i->second;
9432  uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
9433  uint64_t fee = pd.m_amount_in - pd.m_amount_out;
9434  std::string dests;
9435  for (const auto &d: pd.m_dests) {
9436  if (!dests.empty())
9437  dests += ", ";
9438  dests += get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) + ": " + print_etn(d.amount);
9439  }
9440  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
9441  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9442  payment_id = payment_id.substr(0,16);
9443  success_msg_writer() << "Outgoing transaction found";
9444  success_msg_writer() << "txid: " << txid;
9445  success_msg_writer() << "Height: " << pd.m_block_height;
9447  success_msg_writer() << "Amount: " << print_etn(pd.m_amount_in - change - fee);
9448  success_msg_writer() << "Payment ID: " << payment_id;
9449  success_msg_writer() << "Change: " << print_etn(change);
9450  success_msg_writer() << "Fee: " << print_etn(fee);
9451  success_msg_writer() << "Destinations: " << dests;
9452  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9453  return true;
9454  }
9455  }
9456 
9457  try
9458  {
9459  m_wallet->update_pool_state();
9460  std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
9461  m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account);
9462  for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
9463  const tools::wallet2::payment_details &pd = i->second.m_pd;
9464  if (pd.m_tx_hash == txid)
9465  {
9466  std::string payment_id = string_tools::pod_to_hex(i->first);
9467  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9468  payment_id = payment_id.substr(0,16);
9469  success_msg_writer() << "Unconfirmed incoming transaction found in the txpool";
9470  success_msg_writer() << "txid: " << txid;
9472  success_msg_writer() << "Amount: " << print_etn(pd.m_amount);
9473  success_msg_writer() << "Payment ID: " << payment_id;
9474  success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
9475  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9476  if (i->second.m_double_spend_seen)
9477  success_msg_writer() << tr("Double spend seen on the network: this transaction may or may not end up being mined");
9478  if (i->second.m_nonexistent_utxo_seen)
9479  success_msg_writer() << tr("Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
9480  return true;
9481  }
9482  }
9483  }
9484  catch (...)
9485  {
9486  fail_msg_writer() << "Failed to get pool state";
9487  }
9488 
9489  std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
9490  m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account);
9491  for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
9492  if (i->first == txid)
9493  {
9494  const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
9495  uint64_t amount = pd.m_amount_in;
9496  uint64_t fee = amount - pd.m_amount_out;
9497  std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
9498  if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
9499  payment_id = payment_id.substr(0,16);
9501 
9502  success_msg_writer() << (is_failed ? "Failed" : "Pending") << " outgoing transaction found";
9503  success_msg_writer() << "txid: " << txid;
9505  success_msg_writer() << "Amount: " << print_etn(amount - pd.m_change - fee);
9506  success_msg_writer() << "Payment ID: " << payment_id;
9507  success_msg_writer() << "Change: " << print_etn(pd.m_change);
9508  success_msg_writer() << "Fee: " << print_etn(fee);
9509  success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
9510  return true;
9511  }
9512  }
9513 
9514  fail_msg_writer() << tr("Transaction ID not found");
9515  return true;
9516 }
9517 //----------------------------------------------------------------------------------------------------
9518 bool simple_wallet::process_command(const std::vector<std::string> &args)
9519 {
9520  return m_cmd_binder.process_command_vec(args);
9521 }
9522 //----------------------------------------------------------------------------------------------------
9524 {
9525  if (m_in_manual_refresh.load(std::memory_order_relaxed))
9526  {
9527  m_wallet->stop();
9528  }
9529  else
9530  {
9531  stop();
9532  }
9533 }
9534 //----------------------------------------------------------------------------------------------------
9535 void simple_wallet::commit_or_save(std::vector<tools::wallet2::pending_tx>& ptx_vector, bool do_not_relay)
9536 {
9537  size_t i = 0;
9538  while (!ptx_vector.empty())
9539  {
9540  auto & ptx = ptx_vector.back();
9541  const crypto::hash txid = get_transaction_hash(ptx.tx);
9542  if (do_not_relay)
9543  {
9544  cryptonote::blobdata blob;
9545  tx_to_blob(ptx.tx, blob);
9547  const std::string filename = "raw_electroneum_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++)));
9548  if (epee::file_io_utils::save_string_to_file(filename, blob_hex))
9549  success_msg_writer(true) << tr("Transaction successfully saved to ") << filename << tr(", txid ") << txid;
9550  else
9551  fail_msg_writer() << tr("Failed to save transaction to ") << filename << tr(", txid ") << txid;
9552  }
9553  else
9554  {
9555  m_wallet->commit_tx(ptx);
9556  success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << txid << ENDL
9557  << tr("You can check its status by using the `show_transfers` command.");
9558  }
9559  // if no exception, remove element from vector
9560  ptx_vector.pop_back();
9561  }
9562 }
9563 //----------------------------------------------------------------------------------------------------
9564 int main(int argc, char* argv[])
9565 {
9566  TRY_ENTRY();
9567 
9568 #ifdef WIN32
9569  // Activate UTF-8 support for Boost filesystem classes on Windows
9570  std::locale::global(boost::locale::generator().generate(""));
9571  boost::filesystem::path::imbue(std::locale());
9572 #endif
9573  // READ IN OPTIONS FROM COMMAND LINE
9574  po::options_description desc_params(wallet_args::tr("Wallet options"));
9575  tools::wallet2::init_options(desc_params);
9576  command_line::add_arg(desc_params, arg_wallet_file);
9577  command_line::add_arg(desc_params, arg_generate_new_wallet);
9578  command_line::add_arg(desc_params, arg_generate_from_device);
9579  command_line::add_arg(desc_params, arg_generate_from_view_key);
9580  command_line::add_arg(desc_params, arg_generate_from_spend_key);
9581  command_line::add_arg(desc_params, arg_generate_from_keys);
9582  command_line::add_arg(desc_params, arg_generate_from_multisig_keys);
9584  command_line::add_arg(desc_params, arg_mnemonic_language);
9585  command_line::add_arg(desc_params, arg_command);
9586 
9587  command_line::add_arg(desc_params, arg_restore_deterministic_wallet );
9588  command_line::add_arg(desc_params, arg_restore_multisig_wallet );
9589  command_line::add_arg(desc_params, arg_non_deterministic );
9590  command_line::add_arg(desc_params, arg_electrum_seed );
9591  command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version);
9592  command_line::add_arg(desc_params, arg_restore_height);
9593  command_line::add_arg(desc_params, arg_restore_date);
9594  command_line::add_arg(desc_params, arg_do_not_relay);
9595  command_line::add_arg(desc_params, arg_create_address_file);
9596  command_line::add_arg(desc_params, arg_subaddress_lookahead);
9597  command_line::add_arg(desc_params, arg_use_english_language_names);
9598  command_line::add_arg(desc_params, arg_long_payment_id_support);
9599  command_line::add_arg(desc_params, arg_account_major_offset);
9602  po::positional_options_description positional_options;
9603  positional_options.add(arg_command.name, -1);
9604 
9605  boost::optional<po::variables_map> vm;
9606  bool should_terminate = false;
9607  std::tie(vm, should_terminate) = wallet_args::main(
9608  argc, argv,
9609  "electroneum-wallet-cli [--wallet-file=<filename>|--generate-new-wallet=<filename>] [<COMMAND>]",
9610  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."),
9611  desc_params,
9612  positional_options,
9613  [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
9614  "electroneum-wallet-cli.log"
9615  );
9616 
9617  if (!vm)
9618  {
9619  return 1;
9620  }
9621 
9622  if (should_terminate)
9623  {
9624  return 0;
9625  }
9626 
9627 
9628  // INITIALISE THE WALLET AND OPEN IT UP (.init)
9630  const bool r = w.init(*vm);
9631  CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet"));
9632 
9633  std::vector<std::string> command = command_line::get_arg(*vm, arg_command);
9634  if (!command.empty())
9635  {
9636  if (!w.process_command(command))
9637  fail_msg_writer() << sw::tr("Unknown command: ") << command.front();
9638  w.stop();
9639  w.deinit();
9640  }
9641  else
9642  {
9643  tools::signal_handler::install([&w](int type) {
9645  {
9646  // must be prompting for password so return and let the signal stop prompt
9647  return;
9648  }
9649 #ifdef WIN32
9650  if (type == CTRL_C_EVENT)
9651 #else
9652  if (type == SIGINT)
9653 #endif
9654  {
9655  // if we're pressing ^C when refreshing, just stop refreshing
9656  w.interrupt();
9657  }
9658  else
9659  {
9660  w.stop();
9661  }
9662  });
9663 
9664  // FINALLY, RUN THE WALLET
9665  w.run();
9666 
9667  // CLOSE WALLET DOWN WHEN CONTROL RETURNS FROM w.run()
9668  w.deinit();
9669  }
9670  return 0;
9671  CATCH_ENTRY_L0("main", 1);
9672 }
9673 
9674 // MMS ---------------------------------------------------------------------------------------------------
9675 
9676 // Access to the message store, or more exactly to the list of the messages that can be changed
9677 // by the idle thread, is guarded by the same mutex-based mechanism as access to the wallet
9678 // as a whole and thus e.g. uses the "LOCK_IDLE_SCOPE" macro. This is a little over-cautious, but
9679 // simple and safe. Care has to be taken however where MMS methods call other simplewallet methods
9680 // that use "LOCK_IDLE_SCOPE" as this cannot be nested!
9681 
9682 // Methods for commands like "export_multisig_info" usually read data from file(s) or write data
9683 // to files. The MMS calls now those methods as well, to produce data for messages and to process data
9684 // from messages. As it would be quite inconvenient for the MMS to write data for such methods to files
9685 // first or get data out of result files after the call, those methods detect a call from the MMS and
9686 // expect data as arguments instead of files and give back data by calling 'process_wallet_created_data'.
9687 
9688 bool simple_wallet::user_confirms(const std::string &question)
9689 {
9690  std::string answer = input_line(question + tr(" (Y/Yes/N/No): "));
9691  return !std::cin.eof() && command_line::is_yes(answer);
9692 }
9693 
9694 bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound)
9695 {
9696  bool valid = false;
9697  try
9698  {
9699  number = boost::lexical_cast<uint32_t>(arg);
9700  valid = (number >= lower_bound) && (number <= upper_bound);
9701  }
9702  catch(const boost::bad_lexical_cast &)
9703  {
9704  }
9705  return valid;
9706 }
9707 
9708 bool simple_wallet::choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice)
9709 {
9710  size_t choices = data_list.size();
9711  if (choices == 1)
9712  {
9713  choice = 0;
9714  return true;
9715  }
9716  mms::message_store& ms = m_wallet->get_message_store();
9717  message_writer() << tr("Choose processing:");
9718  std::string text;
9719  for (size_t i = 0; i < choices; ++i)
9720  {
9721  const mms::processing_data &data = data_list[i];
9722  text = std::to_string(i+1) + ": ";
9723  switch (data.processing)
9724  {
9726  text += tr("Sign tx");
9727  break;
9729  {
9730  mms::message m;
9731  ms.get_message_by_id(data.message_ids[0], m);
9733  {
9734  text += tr("Send the tx for submission to ");
9735  }
9736  else
9737  {
9738  text += tr("Send the tx for signing to ");
9739  }
9741  text += ms.signer_to_string(signer, 50);
9742  break;
9743  }
9745  text += tr("Submit tx");
9746  break;
9747  default:
9748  text += tr("unknown");
9749  break;
9750  }
9751  message_writer() << text;
9752  }
9753 
9754  std::string line = input_line(tr("Choice: "));
9755  if (std::cin.eof() || line.empty())
9756  {
9757  return false;
9758  }
9759  bool choice_ok = get_number_from_arg(line, choice, 1, choices);
9760  if (choice_ok)
9761  {
9762  choice--;
9763  }
9764  else
9765  {
9766  fail_msg_writer() << tr("Wrong choice");
9767  }
9768  return choice_ok;
9769 }
9770 
9771 void simple_wallet::list_mms_messages(const std::vector<mms::message> &messages)
9772 {
9773  message_writer() << boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") % tr("Id") % tr("I/O") % tr("Authorized Signer")
9774  % tr("Message Type") % tr("Height") % tr("R") % tr("Message State") % tr("Since");
9775  mms::message_store& ms = m_wallet->get_message_store();
9776  uint64_t now = (uint64_t)time(NULL);
9777  for (size_t i = 0; i < messages.size(); ++i)
9778  {
9779  const mms::message &m = messages[i];
9780  const mms::authorized_signer &signer = ms.get_signer(m.signer_index);
9783  boost::format("%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
9784  m.id %
9786  ms.signer_to_string(signer, 30) %
9788  m.wallet_height %
9789  m.round %
9791  (tools::get_human_readable_timestamp(m.modified) + ", " + get_human_readable_timespan(std::chrono::seconds(now - m.modified)) + tr(" ago"));
9792  }
9793 }
9794 
9795 void simple_wallet::list_signers(const std::vector<mms::authorized_signer> &signers)
9796 {
9797  message_writer() << boost::format("%2s %-20s %-s") % tr("#") % tr("Label") % tr("Transport Address");
9798  message_writer() << boost::format("%2s %-20s %-s") % "" % tr("Auto-Config Token") % tr("Electroneum Address");
9799  for (size_t i = 0; i < signers.size(); ++i)
9800  {
9801  const mms::authorized_signer &signer = signers[i];
9802  std::string label = signer.label.empty() ? tr("<not set>") : signer.label;
9803  std::string electroneum_address;
9804  if (signer.etn_address_known)
9805  {
9806  electroneum_address = get_account_address_as_str(m_wallet->nettype(), false, signer.etn_address);
9807  }
9808  else
9809  {
9810  electroneum_address = tr("<not set>");
9811  }
9812  std::string transport_address = signer.transport_address.empty() ? tr("<not set>") : signer.transport_address;
9813  message_writer() << boost::format("%2s %-20s %-s") % (i + 1) % label % transport_address;
9814  message_writer() << boost::format("%2s %-20s %-s") % "" % signer.auto_config_token % electroneum_address;
9815  message_writer() << "";
9816  }
9817 }
9818 
9819 void simple_wallet::add_signer_config_messages()
9820 {
9821  mms::message_store& ms = m_wallet->get_message_store();
9823  ms.get_signer_config(signer_config);
9824 
9825  const std::vector<mms::authorized_signer> signers = ms.get_all_signers();
9826  mms::multisig_wallet_state state = get_multisig_wallet_state();
9827  uint32_t num_authorized_signers = ms.get_num_authorized_signers();
9828  for (uint32_t i = 1 /* without me */; i < num_authorized_signers; ++i)
9829  {
9831  }
9832 }
9833 
9834 void simple_wallet::show_message(const mms::message &m)
9835 {
9836  mms::message_store& ms = m_wallet->get_message_store();
9837  const mms::authorized_signer &signer = ms.get_signer(m.signer_index);
9838  bool display_content;
9839  std::string sanitized_text;
9840  switch (m.type)
9841  {
9845  display_content = true;
9846  ms.get_sanitized_message_text(m, sanitized_text);
9847  break;
9848  default:
9849  display_content = false;
9850  }
9851  uint64_t now = (uint64_t)time(NULL);
9852  message_writer() << "";
9853  message_writer() << tr("Message ") << m.id;
9854  message_writer() << tr("In/out: ") << ms.message_direction_to_string(m.direction);
9855  message_writer() << tr("Type: ") << ms.message_type_to_string(m.type);
9856  message_writer() << tr("State: ") << boost::format(tr("%s since %s, %s ago")) %
9857  ms.message_state_to_string(m.state) % tools::get_human_readable_timestamp(m.modified) % get_human_readable_timespan(std::chrono::seconds(now - m.modified));
9858  if (m.sent == 0)
9859  {
9860  message_writer() << tr("Sent: Never");
9861  }
9862  else
9863  {
9864  message_writer() << boost::format(tr("Sent: %s, %s ago")) %
9865  tools::get_human_readable_timestamp(m.sent) % get_human_readable_timespan(std::chrono::seconds(now - m.sent));
9866  }
9867  message_writer() << tr("Authorized signer: ") << ms.signer_to_string(signer, 100);
9868  message_writer() << tr("Content size: ") << m.content.length() << tr(" bytes");
9869  message_writer() << tr("Content: ") << (display_content ? sanitized_text : tr("(binary data)"));
9870 
9871  if (m.type == mms::message_type::note)
9872  {
9873  // Showing a note and read its text is "processing" it: Set the state accordingly
9874  // which will also delete it from Bitmessage as a side effect
9875  // (Without this little "twist" it would never change the state, and never get deleted)
9877  }
9878 }
9879 
9880 void simple_wallet::ask_send_all_ready_messages()
9881 {
9882  mms::message_store& ms = m_wallet->get_message_store();
9883  std::vector<mms::message> ready_messages;
9884  const std::vector<mms::message> &messages = ms.get_all_messages();
9885  for (size_t i = 0; i < messages.size(); ++i)
9886  {
9887  const mms::message &m = messages[i];
9889  {
9890  ready_messages.push_back(m);
9891  }
9892  }
9893  if (ready_messages.size() != 0)
9894  {
9895  list_mms_messages(ready_messages);
9896  bool send = ms.get_auto_send();
9897  if (!send)
9898  {
9899  send = user_confirms(tr("Send these messages now?"));
9900  }
9901  if (send)
9902  {
9903  mms::multisig_wallet_state state = get_multisig_wallet_state();
9904  for (size_t i = 0; i < ready_messages.size(); ++i)
9905  {
9906  ms.send_message(state, ready_messages[i].id);
9907  ms.set_message_processed_or_sent(ready_messages[i].id);
9908  }
9909  success_msg_writer() << tr("Queued for sending.");
9910  }
9911  }
9912 }
9913 
9914 bool simple_wallet::get_message_from_arg(const std::string &arg, mms::message &m)
9915 {
9916  mms::message_store& ms = m_wallet->get_message_store();
9917  bool valid_id = false;
9918  uint32_t id;
9919  try
9920  {
9921  id = (uint32_t)boost::lexical_cast<uint32_t>(arg);
9922  valid_id = ms.get_message_by_id(id, m);
9923  }
9924  catch (const boost::bad_lexical_cast &)
9925  {
9926  }
9927  if (!valid_id)
9928  {
9929  fail_msg_writer() << tr("Invalid message id");
9930  }
9931  return valid_id;
9932 }
9933 
9934 void simple_wallet::mms_init(const std::vector<std::string> &args)
9935 {
9936  if (args.size() != 3)
9937  {
9938  fail_msg_writer() << tr("usage: mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
9939  return;
9940  }
9941  mms::message_store& ms = m_wallet->get_message_store();
9942  if (ms.get_active())
9943  {
9944  if (!user_confirms(tr("The MMS is already initialized. Re-initialize by deleting all signer info and messages?")))
9945  {
9946  return;
9947  }
9948  }
9949  uint32_t num_required_signers;
9950  uint32_t num_authorized_signers;
9951  const std::string &mn = args[0];
9952  std::vector<std::string> numbers;
9953  boost::split(numbers, mn, boost::is_any_of("/"));
9954  bool mn_ok = (numbers.size() == 2)
9955  && get_number_from_arg(numbers[1], num_authorized_signers, 2, 100)
9956  && get_number_from_arg(numbers[0], num_required_signers, 2, num_authorized_signers);
9957  if (!mn_ok)
9958  {
9959  fail_msg_writer() << tr("Error in the number of required signers and/or authorized signers");
9960  return;
9961  }
9962  LOCK_IDLE_SCOPE();
9963  ms.init(get_multisig_wallet_state(), args[1], args[2], num_authorized_signers, num_required_signers);
9964 }
9965 
9966 void simple_wallet::mms_info(const std::vector<std::string> &args)
9967 {
9968  mms::message_store& ms = m_wallet->get_message_store();
9969  if (ms.get_active())
9970  {
9971  message_writer() << boost::format("The MMS is active for %s/%s multisig.")
9973  }
9974  else
9975  {
9976  message_writer() << tr("The MMS is not active.");
9977  }
9978 }
9979 
9980 void simple_wallet::mms_signer(const std::vector<std::string> &args)
9981 {
9982  mms::message_store& ms = m_wallet->get_message_store();
9983  const std::vector<mms::authorized_signer> &signers = ms.get_all_signers();
9984  if (args.size() == 0)
9985  {
9986  // Without further parameters list all defined signers
9987  list_signers(signers);
9988  return;
9989  }
9990 
9991  uint32_t index;
9992  bool index_valid = get_number_from_arg(args[0], index, 1, ms.get_num_authorized_signers());
9993  if (index_valid)
9994  {
9995  index--;
9996  }
9997  else
9998  {
9999  fail_msg_writer() << tr("Invalid signer number ") + args[0];
10000  return;
10001  }
10002  if ((args.size() < 2) || (args.size() > 4))
10003  {
10004  fail_msg_writer() << tr("mms signer [<number> <label> [<transport_address> [<electroneum_address>]]]");
10005  return;
10006  }
10007 
10008  boost::optional<string> label = args[1];
10009  boost::optional<string> transport_address;
10010  if (args.size() >= 3)
10011  {
10012  transport_address = args[2];
10013  }
10014  boost::optional<cryptonote::account_public_address> electroneum_address;
10015  LOCK_IDLE_SCOPE();
10016  mms::multisig_wallet_state state = get_multisig_wallet_state();
10017  if (args.size() == 4)
10018  {
10020  bool ok = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[3], oa_prompter);
10021  if (!ok)
10022  {
10023  fail_msg_writer() << tr("Invalid Electroneum address");
10024  return;
10025  }
10026  electroneum_address = info.address;
10027  const std::vector<mms::message> &messages = ms.get_all_messages();
10028  if ((messages.size() > 0) || state.multisig)
10029  {
10030  fail_msg_writer() << tr("Wallet state does not allow changing Electroneum addresses anymore");
10031  return;
10032  }
10033  }
10034  ms.set_signer(state, index, label, transport_address, electroneum_address);
10035 }
10036 
10037 void simple_wallet::mms_list(const std::vector<std::string> &args)
10038 {
10039  mms::message_store& ms = m_wallet->get_message_store();
10040  if (args.size() != 0)
10041  {
10042  fail_msg_writer() << tr("Usage: mms list");
10043  return;
10044  }
10045  LOCK_IDLE_SCOPE();
10046  const std::vector<mms::message> &messages = ms.get_all_messages();
10047  list_mms_messages(messages);
10048 }
10049 
10050 void simple_wallet::mms_next(const std::vector<std::string> &args)
10051 {
10052  mms::message_store& ms = m_wallet->get_message_store();
10053  if ((args.size() > 1) || ((args.size() == 1) && (args[0] != "sync")))
10054  {
10055  fail_msg_writer() << tr("Usage: mms next [sync]");
10056  return;
10057  }
10058  bool avail = false;
10059  std::vector<mms::processing_data> data_list;
10060  bool force_sync = false;
10061  uint32_t choice = 0;
10062  {
10063  LOCK_IDLE_SCOPE();
10064  if ((args.size() == 1) && (args[0] == "sync"))
10065  {
10066  // Force the MMS to process any waiting sync info although on its own it would just ignore
10067  // those messages because no need to process them can be seen
10068  force_sync = true;
10069  }
10070  string wait_reason;
10071  {
10072  avail = ms.get_processable_messages(get_multisig_wallet_state(), force_sync, data_list, wait_reason);
10073  }
10074  if (avail)
10075  {
10076  avail = choose_mms_processing(data_list, choice);
10077  }
10078  else if (!wait_reason.empty())
10079  {
10080  message_writer() << tr("No next step: ") << wait_reason;
10081  }
10082  }
10083  if (avail)
10084  {
10085  mms::processing_data data = data_list[choice];
10086  bool command_successful = false;
10087  switch(data.processing)
10088  {
10090  message_writer() << tr("prepare_multisig");
10091  command_successful = prepare_multisig_main(std::vector<std::string>(), true);
10092  break;
10093 
10095  {
10096  message_writer() << tr("make_multisig");
10097  size_t number_of_key_sets = data.message_ids.size();
10098  std::vector<std::string> sig_args(number_of_key_sets + 1);
10099  sig_args[0] = std::to_string(ms.get_num_required_signers());
10100  for (size_t i = 0; i < number_of_key_sets; ++i)
10101  {
10102  mms::message m = ms.get_message_by_id(data.message_ids[i]);
10103  sig_args[i+1] = m.content;
10104  }
10105  command_successful = make_multisig_main(sig_args, true);
10106  break;
10107  }
10108 
10110  {
10111  message_writer() << tr("exchange_multisig_keys");
10112  size_t number_of_key_sets = data.message_ids.size();
10113  // Other than "make_multisig" only the key sets as parameters, no num_required_signers
10114  std::vector<std::string> sig_args(number_of_key_sets);
10115  for (size_t i = 0; i < number_of_key_sets; ++i)
10116  {
10117  mms::message m = ms.get_message_by_id(data.message_ids[i]);
10118  sig_args[i] = m.content;
10119  }
10120  command_successful = exchange_multisig_keys_main(sig_args, true);
10121  break;
10122  }
10123 
10125  {
10126  message_writer() << tr("export_multisig_info");
10127  std::vector<std::string> export_args;
10128  export_args.push_back("MMS"); // dummy filename
10129  command_successful = export_multisig_main(export_args, true);
10130  break;
10131  }
10132 
10134  {
10135  message_writer() << tr("import_multisig_info");
10136  std::vector<std::string> import_args;
10137  for (size_t i = 0; i < data.message_ids.size(); ++i)
10138  {
10139  mms::message m = ms.get_message_by_id(data.message_ids[i]);
10140  import_args.push_back(m.content);
10141  }
10142  command_successful = import_multisig_main(import_args, true);
10143  break;
10144  }
10145 
10147  {
10148  message_writer() << tr("sign_multisig");
10149  std::vector<std::string> sign_args;
10150  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10151  sign_args.push_back(m.content);
10152  command_successful = sign_multisig_main(sign_args, true);
10153  break;
10154  }
10155 
10157  {
10158  message_writer() << tr("submit_multisig");
10159  std::vector<std::string> submit_args;
10160  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10161  submit_args.push_back(m.content);
10162  command_successful = submit_multisig_main(submit_args, true);
10163  break;
10164  }
10165 
10167  {
10168  message_writer() << tr("Send tx");
10169  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10170  LOCK_IDLE_SCOPE();
10171  ms.add_message(get_multisig_wallet_state(), data.receiving_signer_index, m.type, mms::message_direction::out,
10172  m.content);
10173  command_successful = true;
10174  break;
10175  }
10176 
10178  {
10179  message_writer() << tr("Process signer config");
10180  LOCK_IDLE_SCOPE();
10181  mms::message m = ms.get_message_by_id(data.message_ids[0]);
10183  mms::multisig_wallet_state state = get_multisig_wallet_state();
10184  if (!me.auto_config_running)
10185  {
10186  // If no auto-config is running, the config sent may be unsolicited or problematic
10187  // so show what arrived and ask for confirmation before taking it in
10188  std::vector<mms::authorized_signer> signers;
10189  ms.unpack_signer_config(state, m.content, signers);
10190  list_signers(signers);
10191  if (!user_confirms(tr("Replace current signer config with the one displayed above?")))
10192  {
10193  break;
10194  }
10195  }
10197  ms.stop_auto_config();
10198  list_signers(ms.get_all_signers());
10199  command_successful = true;
10200  break;
10201  }
10202 
10204  {
10205  message_writer() << tr("Process auto config data");
10206  LOCK_IDLE_SCOPE();
10207  for (size_t i = 0; i < data.message_ids.size(); ++i)
10208  {
10210  }
10211  ms.stop_auto_config();
10212  list_signers(ms.get_all_signers());
10213  add_signer_config_messages();
10214  command_successful = true;
10215  break;
10216  }
10217 
10218  default:
10219  message_writer() << tr("Nothing ready to process");
10220  break;
10221  }
10222 
10223  if (command_successful)
10224  {
10225  {
10226  LOCK_IDLE_SCOPE();
10227  ms.set_messages_processed(data);
10228  ask_send_all_ready_messages();
10229  }
10230  }
10231  }
10232 }
10233 
10234 void simple_wallet::mms_sync(const std::vector<std::string> &args)
10235 {
10236  mms::message_store& ms = m_wallet->get_message_store();
10237  if (args.size() != 0)
10238  {
10239  fail_msg_writer() << tr("Usage: mms sync");
10240  return;
10241  }
10242  // Force the start of a new sync round, for exceptional cases where something went wrong
10243  // Can e.g. solve the problem "This signature was made with stale data" after trying to
10244  // create 2 transactions in a row somehow
10245  // Code is identical to the code for 'message_processing::create_sync_data'
10246  message_writer() << tr("export_multisig_info");
10247  std::vector<std::string> export_args;
10248  export_args.push_back("MMS"); // dummy filename
10249  export_multisig_main(export_args, true);
10250  ask_send_all_ready_messages();
10251 }
10252 
10253 void simple_wallet::mms_transfer(const std::vector<std::string> &args)
10254 {
10255  // It's too complicated to check any arguments here, just let 'transfer_main' do the whole job
10256  transfer_main(Transfer, args, true);
10257 }
10258 
10259 void simple_wallet::mms_delete(const std::vector<std::string> &args)
10260 {
10261  if (args.size() != 1)
10262  {
10263  fail_msg_writer() << tr("Usage: mms delete (<message_id> | all)");
10264  return;
10265  }
10266  LOCK_IDLE_SCOPE();
10267  mms::message_store& ms = m_wallet->get_message_store();
10268  if (args[0] == "all")
10269  {
10270  if (user_confirms(tr("Delete all messages?")))
10271  {
10272  ms.delete_all_messages();
10273  }
10274  }
10275  else
10276  {
10277  mms::message m;
10278  bool valid_id = get_message_from_arg(args[0], m);
10279  if (valid_id)
10280  {
10281  // If only a single message and not all delete even if unsent / unprocessed
10282  ms.delete_message(m.id);
10283  }
10284  }
10285 }
10286 
10287 void simple_wallet::mms_send(const std::vector<std::string> &args)
10288 {
10289  if (args.size() == 0)
10290  {
10291  ask_send_all_ready_messages();
10292  return;
10293  }
10294  else if (args.size() != 1)
10295  {
10296  fail_msg_writer() << tr("Usage: mms send [<message_id>]");
10297  return;
10298  }
10299  LOCK_IDLE_SCOPE();
10300  mms::message_store& ms = m_wallet->get_message_store();
10301  mms::message m;
10302  bool valid_id = get_message_from_arg(args[0], m);
10303  if (valid_id)
10304  {
10305  ms.send_message(get_multisig_wallet_state(), m.id);
10306  }
10307 }
10308 
10309 void simple_wallet::mms_receive(const std::vector<std::string> &args)
10310 {
10311  if (args.size() != 0)
10312  {
10313  fail_msg_writer() << tr("Usage: mms receive");
10314  return;
10315  }
10316  std::vector<mms::message> new_messages;
10317  LOCK_IDLE_SCOPE();
10318  mms::message_store& ms = m_wallet->get_message_store();
10319  bool avail = ms.check_for_messages(get_multisig_wallet_state(), new_messages);
10320  if (avail)
10321  {
10322  list_mms_messages(new_messages);
10323  }
10324 }
10325 
10326 void simple_wallet::mms_export(const std::vector<std::string> &args)
10327 {
10328  if (args.size() != 1)
10329  {
10330  fail_msg_writer() << tr("Usage: mms export <message_id>");
10331  return;
10332  }
10333  LOCK_IDLE_SCOPE();
10334  mms::message_store& ms = m_wallet->get_message_store();
10335  mms::message m;
10336  bool valid_id = get_message_from_arg(args[0], m);
10337  if (valid_id)
10338  {
10339  const std::string filename = "mms_message_content";
10341  {
10342  success_msg_writer() << tr("Message content saved to: ") << filename;
10343  }
10344  else
10345  {
10346  fail_msg_writer() << tr("Failed to to save message content");
10347  }
10348  }
10349 }
10350 
10351 void simple_wallet::mms_note(const std::vector<std::string> &args)
10352 {
10353  mms::message_store& ms = m_wallet->get_message_store();
10354  if (args.size() == 0)
10355  {
10356  LOCK_IDLE_SCOPE();
10357  const std::vector<mms::message> &messages = ms.get_all_messages();
10358  for (size_t i = 0; i < messages.size(); ++i)
10359  {
10360  const mms::message &m = messages[i];
10362  {
10363  show_message(m);
10364  }
10365  }
10366  return;
10367  }
10368  if (args.size() < 2)
10369  {
10370  fail_msg_writer() << tr("Usage: mms note [<label> <text>]");
10371  return;
10372  }
10373  uint32_t signer_index;
10374  bool found = ms.get_signer_index_by_label(args[0], signer_index);
10375  if (!found)
10376  {
10377  fail_msg_writer() << tr("No signer found with label ") << args[0];
10378  return;
10379  }
10380  std::string note = "";
10381  for (size_t n = 1; n < args.size(); ++n)
10382  {
10383  if (n > 1)
10384  {
10385  note += " ";
10386  }
10387  note += args[n];
10388  }
10389  LOCK_IDLE_SCOPE();
10390  ms.add_message(get_multisig_wallet_state(), signer_index, mms::message_type::note,
10392  ask_send_all_ready_messages();
10393 }
10394 
10395 void simple_wallet::mms_show(const std::vector<std::string> &args)
10396 {
10397  if (args.size() != 1)
10398  {
10399  fail_msg_writer() << tr("Usage: mms show <message_id>");
10400  return;
10401  }
10402  LOCK_IDLE_SCOPE();
10403  mms::message_store& ms = m_wallet->get_message_store();
10404  mms::message m;
10405  bool valid_id = get_message_from_arg(args[0], m);
10406  if (valid_id)
10407  {
10408  show_message(m);
10409  }
10410 }
10411 
10412 void simple_wallet::mms_set(const std::vector<std::string> &args)
10413 {
10414  bool set = args.size() == 2;
10415  bool query = args.size() == 1;
10416  if (!set && !query)
10417  {
10418  fail_msg_writer() << tr("Usage: mms set <option_name> [<option_value>]");
10419  return;
10420  }
10421  mms::message_store& ms = m_wallet->get_message_store();
10422  LOCK_IDLE_SCOPE();
10423  if (args[0] == "auto-send")
10424  {
10425  if (set)
10426  {
10427  bool result;
10428  bool ok = parse_bool(args[1], result);
10429  if (ok)
10430  {
10431  ms.set_auto_send(result);
10432  }
10433  else
10434  {
10435  fail_msg_writer() << tr("Wrong option value");
10436  }
10437  }
10438  else
10439  {
10440  message_writer() << (ms.get_auto_send() ? tr("Auto-send is on") : tr("Auto-send is off"));
10441  }
10442  }
10443  else
10444  {
10445  fail_msg_writer() << tr("Unknown option");
10446  }
10447 }
10448 
10449 void simple_wallet::mms_help(const std::vector<std::string> &args)
10450 {
10451  if (args.size() > 1)
10452  {
10453  fail_msg_writer() << tr("Usage: mms help [<subcommand>]");
10454  return;
10455  }
10456  std::vector<std::string> help_args;
10457  help_args.push_back("mms");
10458  if (args.size() == 1)
10459  {
10460  help_args.push_back(args[0]);
10461  }
10462  help(help_args);
10463 }
10464 
10465 void simple_wallet::mms_send_signer_config(const std::vector<std::string> &args)
10466 {
10467  if (args.size() != 0)
10468  {
10469  fail_msg_writer() << tr("Usage: mms send_signer_config");
10470  return;
10471  }
10472  mms::message_store& ms = m_wallet->get_message_store();
10473  if (!ms.signer_config_complete())
10474  {
10475  fail_msg_writer() << tr("Signer config not yet complete");
10476  return;
10477  }
10478  LOCK_IDLE_SCOPE();
10479  add_signer_config_messages();
10480  ask_send_all_ready_messages();
10481 }
10482 
10483 void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args)
10484 {
10485  mms::message_store& ms = m_wallet->get_message_store();
10486  uint32_t other_signers = ms.get_num_authorized_signers() - 1;
10487  size_t args_size = args.size();
10488  if ((args_size != 0) && (args_size != other_signers))
10489  {
10490  fail_msg_writer() << tr("Usage: mms start_auto_config [<label> <label> ...]");
10491  return;
10492  }
10493  if ((args_size == 0) && !ms.signer_labels_complete())
10494  {
10495  fail_msg_writer() << tr("There are signers without a label set. Complete labels before auto-config or specify them as parameters here.");
10496  return;
10497  }
10499  if (me.auto_config_running)
10500  {
10501  if (!user_confirms(tr("Auto-config is already running. Cancel and restart?")))
10502  {
10503  return;
10504  }
10505  }
10506  LOCK_IDLE_SCOPE();
10507  mms::multisig_wallet_state state = get_multisig_wallet_state();
10508  if (args_size != 0)
10509  {
10510  // Set (or overwrite) all the labels except "me" from the arguments
10511  for (uint32_t i = 1; i < (other_signers + 1); ++i)
10512  {
10513  ms.set_signer(state, i, args[i - 1], boost::none, boost::none);
10514  }
10515  }
10517  // List the signers to show the generated auto-config tokens
10518  list_signers(ms.get_all_signers());
10519 }
10520 
10521 void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args)
10522 {
10523  if (args.size() != 0)
10524  {
10525  fail_msg_writer() << tr("Usage: mms stop_auto_config");
10526  return;
10527  }
10528  if (!user_confirms(tr("Delete any auto-config tokens and stop auto-config?")))
10529  {
10530  return;
10531  }
10532  mms::message_store& ms = m_wallet->get_message_store();
10533  LOCK_IDLE_SCOPE();
10534  ms.stop_auto_config();
10535 }
10536 
10537 void simple_wallet::mms_auto_config(const std::vector<std::string> &args)
10538 {
10539  if (args.size() != 1)
10540  {
10541  fail_msg_writer() << tr("Usage: mms auto_config <auto_config_token>");
10542  return;
10543  }
10544  mms::message_store& ms = m_wallet->get_message_store();
10545  std::string adjusted_token;
10546  if (!ms.check_auto_config_token(args[0], adjusted_token))
10547  {
10548  fail_msg_writer() << tr("Invalid auto-config token");
10549  return;
10550  }
10552  if (me.auto_config_running)
10553  {
10554  if (!user_confirms(tr("Auto-config already running. Cancel and restart?")))
10555  {
10556  return;
10557  }
10558  }
10559  LOCK_IDLE_SCOPE();
10560  ms.add_auto_config_data_message(get_multisig_wallet_state(), adjusted_token);
10561  ask_send_all_ready_messages();
10562 }
10563 
10564 bool simple_wallet::mms(const std::vector<std::string> &args)
10565 {
10566  try
10567  {
10568  m_wallet->get_multisig_wallet_state();
10569  }
10570  catch(const std::exception &e)
10571  {
10572  fail_msg_writer() << tr("MMS not available in this wallet");
10573  return true;
10574  }
10575 
10576  try
10577  {
10578  mms::message_store& ms = m_wallet->get_message_store();
10579  if (args.size() == 0)
10580  {
10581  mms_info(args);
10582  return true;
10583  }
10584 
10585  const std::string &sub_command = args[0];
10586  std::vector<std::string> mms_args = args;
10587  mms_args.erase(mms_args.begin());
10588 
10589  if (sub_command == "init")
10590  {
10591  mms_init(mms_args);
10592  return true;
10593  }
10594  if (!ms.get_active())
10595  {
10596  fail_msg_writer() << tr("The MMS is not active. Activate using the \"mms init\" command");
10597  return true;
10598  }
10599  else if (sub_command == "info")
10600  {
10601  mms_info(mms_args);
10602  }
10603  else if (sub_command == "signer")
10604  {
10605  mms_signer(mms_args);
10606  }
10607  else if (sub_command == "list")
10608  {
10609  mms_list(mms_args);
10610  }
10611  else if (sub_command == "next")
10612  {
10613  mms_next(mms_args);
10614  }
10615  else if (sub_command == "sync")
10616  {
10617  mms_sync(mms_args);
10618  }
10619  else if (sub_command == "transfer")
10620  {
10621  mms_transfer(mms_args);
10622  }
10623  else if (sub_command == "delete")
10624  {
10625  mms_delete(mms_args);
10626  }
10627  else if (sub_command == "send")
10628  {
10629  mms_send(mms_args);
10630  }
10631  else if (sub_command == "receive")
10632  {
10633  mms_receive(mms_args);
10634  }
10635  else if (sub_command == "export")
10636  {
10637  mms_export(mms_args);
10638  }
10639  else if (sub_command == "note")
10640  {
10641  mms_note(mms_args);
10642  }
10643  else if (sub_command == "show")
10644  {
10645  mms_show(mms_args);
10646  }
10647  else if (sub_command == "set")
10648  {
10649  mms_set(mms_args);
10650  }
10651  else if (sub_command == "help")
10652  {
10653  mms_help(mms_args);
10654  }
10655  else if (sub_command == "send_signer_config")
10656  {
10657  mms_send_signer_config(mms_args);
10658  }
10659  else if (sub_command == "start_auto_config")
10660  {
10661  mms_start_auto_config(mms_args);
10662  }
10663  else if (sub_command == "stop_auto_config")
10664  {
10665  mms_stop_auto_config(mms_args);
10666  }
10667  else if (sub_command == "auto_config")
10668  {
10669  mms_auto_config(mms_args);
10670  }
10671  else
10672  {
10673  fail_msg_writer() << tr("Invalid MMS subcommand");
10674  }
10675  }
10676  catch (const tools::error::no_connection_to_daemon &e)
10677  {
10678  fail_msg_writer() << tr("Error in MMS command: ") << e.what() << " " << e.request();
10679  }
10680  catch (const std::exception &e)
10681  {
10682  fail_msg_writer() << tr("Error in MMS command: ") << e.what();
10683  PRINT_USAGE(USAGE_MMS);
10684  return true;
10685  }
10686  return true;
10687 }
10688 // End MMS ------------------------------------------------------------------------------------------------
10689 
return true
uint64_t height
Definition: blockchain.cpp:91
time_t time
Definition: blockchain.cpp:93
uint8_t threshold
Definition: blockchain.cpp:92
unsigned char u8
Definition: chacha_private.h:9
Manages wallet operations. This is the most abstracted wallet class.
Definition: simplewallet.h:68
static const char * tr(const char *str)
Definition: simplewallet.h:70
bool init(const boost::program_options::variables_map &vm)
bool process_command(const std::vector< std::string > &args)
std::vector< uint8_t > extra
bool process_command_vec(const std::vector< std::string > &cmd)
void set_handler(const std::string &cmd, const callback &hndlr, const std::string &usage="", const std::string &description="")
bool run_handling(std::function< std::string(void)> prompt, const std::string &usage_string, std::function< void(void)> exit_handler=NULL)
bool hex_to_pod(T &pod) const
void split(std::vector< wipeable_string > &fields) const
const char * data() const noexcept
boost::optional< wipeable_string > parse_hexstr() const
bool empty() const noexcept
size_t size() const noexcept
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)
void process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
bool get_signer_index_by_label(const std::string label, uint32_t &index) const
void process_auto_config_data_message(uint32_t id)
bool check_for_messages(const multisig_wallet_state &state, std::vector< message > &messages)
bool get_auto_send() const
const std::vector< message > & get_all_messages() const
static const char * message_direction_to_string(message_direction direction)
bool get_processable_messages(const multisig_wallet_state &state, bool force_sync, std::vector< processing_data > &data_list, std::string &wait_reason)
void send_message(const multisig_wallet_state &state, uint32_t id)
bool check_auto_config_token(const std::string &raw_token, std::string &adjusted_token) const
size_t add_message(const multisig_wallet_state &state, uint32_t signer_index, message_type type, message_direction direction, const std::string &content)
bool signer_labels_complete() const
size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token)
void set_message_processed_or_sent(uint32_t id)
void get_signer_config(std::string &signer_config)
std::string signer_to_string(const authorized_signer &signer, uint32_t max_width)
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content)
const std::vector< authorized_signer > & get_all_signers() const
uint32_t get_num_required_signers() const
void set_auto_send(bool auto_send)
void start_auto_config(const multisig_wallet_state &state)
static const char * message_state_to_string(message_state state)
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)
uint32_t get_num_authorized_signers() const
bool signer_config_complete() const
bool get_active() const
const authorized_signer & get_signer(uint32_t index) const
void delete_message(uint32_t id)
void unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, std::vector< authorized_signer > &signers)
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const
static const char * message_type_to_string(message_type type)
bool get_message_by_id(uint32_t id, message &m) const
void set_messages_processed(const processing_data &data)
static boost::optional< password_container > prompt(bool verify, const char *mesage="Password", bool hide_input=true)
Definition: password.cpp:253
static std::atomic< bool > is_prompting
Definition: password.h:55
static bool install(T t)
installs a signal handler
Definition: util.h:164
static const char * tr(const char *str)
Definition: wallet2.cpp:993
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1172
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:1249
static bool parse_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition: wallet2.cpp:5628
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1187
@ AskPasswordToDecrypt
Definition: wallet2.h:228
@ AskPasswordOnAction
Definition: wallet2.h:227
@ AskPasswordNever
Definition: wallet2.h:226
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:1226
static std::string device_name_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1182
std::vector< transfer_details > transfer_container
Definition: wallet2.h:443
@ RefreshOptimizeCoinbase
Definition: wallet2.h:220
@ RefreshNoCoinbase
Definition: wallet2.h:221
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:1232
static bool parse_long_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition: wallet2.cpp:5602
static bool parse_short_payment_id(const std::string &payment_id_str, crypto::hash8 &payment_id)
Definition: wallet2.cpp:5615
BackgroundMiningSetupType
Definition: wallet2.h:231
@ BackgroundMiningNo
Definition: wallet2.h:234
@ BackgroundMiningMaybe
Definition: wallet2.h:232
@ BackgroundMiningYes
Definition: wallet2.h:233
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1177
static bool wallet_valid_path_format(const std::string &file_path)
Definition: wallet2.cpp:5597
static void init_options(boost::program_options::options_description &desc_params)
Definition: wallet2.cpp:1192
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:5587
#define tr(x)
Definition: common_defines.h:4
#define MAKE_CORE_RPC_VERSION(major, minor)
#define CORE_RPC_VERSION_MAJOR
#define CORE_RPC_STATUS_OK
#define CORE_RPC_STATUS_BUSY
void sc_reduce32(unsigned char *)
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
#define HF_VERSION_ENFORCE_0_DECOY_TXS
#define DIFFICULTY_TARGET_V6
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
#define HF_VERSION_ENFORCE_0_DECOY_TXS_END
#define CRYPTONOTE_MAX_BLOCK_NUMBER
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
#define HF_VERSION_PER_BYTE_FEE
This is the orginal cryptonote protocol network-events handler, modified by us.
#define F(s)
Mnemonic seed generation and wallet restoration from them.
expect< void > success() noexcept
Definition: expect.h:397
void * memcpy(void *a, const void *b, size_t c)
const char * res
Definition: hmac_keccak.cpp:41
const char * key
Definition: hmac_keccak.cpp:39
#define PRIu64
Definition: inttypes.h:142
void verbose(enum verbosity_value level, const char *format,...) ATTR_FORMAT(printf
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:53
#define MERROR(x)
Definition: misc_log_ex.h:73
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
#define MWARNING(x)
Definition: misc_log_ex.h:74
std::string mlog_get_categories()
Definition: mlog.cpp:276
void mlog_set_log(const char *log)
Definition: mlog.cpp:288
void mlog_set_log_level(int level)
Definition: mlog.cpp:282
#define CATCH_ENTRY_L0(lacation, return_val)
Definition: misc_log_ex.h:165
#define ENDL
Definition: misc_log_ex.h:149
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
#define MINFO(x)
Definition: misc_log_ex.h:75
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
void save(Archive &a, const std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
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
bool is_no(const std::string &str)
bool is_yes(const std::string &str)
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
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
std::vector< const Language::Base * > get_language_list()
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).
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
std::string get_english_name_for(const std::string &name)
Returns the name of a language in English.
const std::string old_language_name
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.
POD_CLASS signature
Definition: crypto.h:108
POD_CLASS hash8
Definition: hash.h:53
POD_CLASS public_key
Definition: crypto.h:76
POD_CLASS key_image
Definition: crypto.h:102
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
Definition: crypto.h:260
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:312
POD_CLASS hash
Definition: hash.h:50
void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant=0, uint64_t height=0)
Definition: hash.h:79
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
unsigned int get_default_decimal_point()
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
const config_t & get_config(network_type nettype)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
const command_line::arg_descriptor< std::string > arg_fallback_to_pow_checkpoint_hash
crypto::hash get_transaction_hash(const transaction &t)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
const command_line::arg_descriptor< uint64_t > arg_fallback_to_pow_checkpoint_height
void set_default_decimal_point(unsigned int decimal_point)
blobdata tx_to_blob(const transaction &tx)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::string blobdata
Definition: blobdatatype.h:39
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
std::string hex(difficulty_type v)
Definition: difficulty.cpp:254
std::string print_etn(uint64_t amount, unsigned int decimal_point)
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::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
std::string get_unit(unsigned int decimal_point)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
const command_line::arg_descriptor< std::vector< std::string > > arg_command
const base::type::char_t * unit
const char * name
@ Info
Mainly useful to represent current progress of application.
@ Error
Information representing errors in application but application will keep running.
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
Definition: file_io_utils.h:73
bool is_file_exist(const std::string &path)
Definition: file_io_utils.h:66
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
std::string to_string(t_connection_type type)
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
Definition: string_tools.h:92
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
std::string & trim(std::string &str)
Definition: string_tools.h:288
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
Definition: string_tools.h:125
std::string buff_to_hex_nodelimer(const std::string &src)
Definition: string_tools.h:87
T & unwrap(mlocked< T > &src)
Definition: mlocker.h:80
console_colors
Definition: misc_log_ex.h:207
@ console_color_red
Definition: misc_log_ex.h:210
@ console_color_magenta
Definition: misc_log_ex.h:214
@ console_color_yellow
Definition: misc_log_ex.h:215
@ console_color_default
Definition: misc_log_ex.h:208
@ console_color_green
Definition: misc_log_ex.h:211
@ console_color_white
Definition: misc_log_ex.h:209
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
mdb_size_t count(MDB_cursor *cur)
error
Tracks LMDB error codes.
Definition: error.h:45
message_type
Definition: message_store.h:56
version
Supported socks variants.
Definition: socks.h:58
expect< void > send(const epee::span< const std::uint8_t > payload, void *const socket, const int flags) noexcept
Definition: zmq.cpp:182
::std::string string
Definition: gtest-port.h:1097
const T & move(const T &t)
Definition: gtest-port.h:1317
scoped_message_writer fail_msg_writer()
std::string get_human_readable_timestamp(uint64_t ts)
Definition: util.cpp:1077
scoped_message_writer msg_writer(epee::console_colors color=epee::console_color_default)
boost::optional< std::pair< uint32_t, uint32_t > > parse_subaddress_lookahead(const std::string &str)
Definition: util.cpp:977
bool is_local_address(const std::string &address)
Definition: util.cpp:874
scoped_message_writer success_msg_writer(bool color=true)
const char * tr(const char *str)
Definition: wallet_args.cpp:81
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
command_line::arg_descriptor< std::string > arg_generate_from_json()
Definition: wallet_args.cpp:72
command_line::arg_descriptor< std::string > arg_wallet_file()
Definition: wallet_args.cpp:76
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1225
const CharType(& source)[N]
Definition: pointer.h:1147
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
for(i=1;i< 1;++i) fe_sq(t0
#define PAUSE_READLINE()
int main(int argc, char *argv[])
bool parse_priority(const std::string &arg, uint32_t &priority)
#define LONG_PAYMENT_ID_SUPPORT_CHECK()
#define LOCK_IDLE_SCOPE()
#define MIN_RING_SIZE
#define SCOPED_WALLET_UNLOCK()
std::string join_priority_strings(const char *delimiter)
cryptonote::simple_wallet sw
TransferType
@ Transfer
@ TransferLocked
#define PRINT_USAGE(usage_help)
#define CHECK_SIMPLE_VARIABLE(name, f, help)
#define DEFAULT_MIX
#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code)
Header file that declares simple_wallet class.
constexpr const char ETN_DONATION_ADDR[]
Definition: simplewallet.h:56
const char * buf
Definition: slow_memmem.cpp:74
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
#define false
Definition: stdbool.h:38
unsigned short uint16_t
Definition: stdint.h:125
signed __int64 int64_t
Definition: stdint.h:135
unsigned int uint32_t
Definition: stdint.h:126
unsigned char uint8_t
Definition: stdint.h:124
unsigned __int64 uint64_t
Definition: stdint.h:136
uint16_t const RPC_DEFAULT_PORT
bool is_subaddress
bool is_integrated
std::string original
uint64_t amount
account_public_address addr
crypto::key_image k_image
std::vector< uint64_t > key_offsets
std::string transport_address
std::string auto_config_token
cryptonote::account_public_address etn_address
uint64_t modified
uint32_t signer_index
uint32_t wallet_height
message_state state
uint32_t round
message_type type
std::string content
uint64_t sent
message_direction direction
std::vector< uint32_t > message_ids
uint32_t receiving_signer_index
message_processing processing
Definition: blake256.h:37
const scanty_outs_t & scanty_outs() const
const cryptonote::transaction & tx() const
const std::string & reason() const
std::string to_string() const
std::string to_string() const
const std::string & request() const
std::set< uint32_t > m_subaddr_indices
Definition: wallet2.h:405
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:400
std::unordered_set< crypto::public_key > m_signers
Definition: wallet2.h:507
std::vector< pending_tx > m_ptx
Definition: wallet2.h:506
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:362
std::vector< crypto::key_image > key_images
Definition: wallet2.h:500
std::vector< pending_tx > ptx
Definition: wallet2.h:499
crypto::key_image m_key_image
Definition: wallet2.h:311
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:318
const crypto::public_key & get_public_key() const
Definition: wallet2.h:326
std::vector< uint8_t > extra
Definition: wallet2.h:420
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:416
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:418
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:424
std::vector< size_t > selected_transfers
Definition: wallet2.h:419
std::set< uint32_t > subaddr_indices
Definition: wallet2.h:426
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:417
enum tools::wallet2::unconfirmed_transfer_details::@62 m_state
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:385
std::vector< tx_construction_data > txes
Definition: wallet2.h:493
std::pair< size_t, wallet2::transfer_container > transfers
Definition: wallet2.h:494
const char * address
Definition: multisig.cpp:37
const char * spendkey
Definition: multisig.cpp:38
const char *const ELECTRONEUM_RELEASE_NAME
const char *const ELECTRONEUM_VERSION_FULL
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)