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