39 #define BOOST_BIND_GLOBAL_PLACEHOLDERS 1
40 #include <boost/bind.hpp>
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>
77 #include <boost/locale.hpp>
78 #include <boost/filesystem.hpp>
88 using boost::lexical_cast;
89 namespace po = boost::program_options;
92 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
93 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.simplewallet"
95 #define EXTENDED_LOGS_FILE "wallet_details.log"
99 #define MIN_RING_SIZE 1
100 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003"
102 #define LOCK_IDLE_SCOPE() \
103 bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
104 m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
107 boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
108 m_idle_cond.notify_all(); \
109 epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
110 m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
113 #define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \
115 boost::optional<tools::password_container> pwd_container = boost::none; \
116 if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \
117 tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
119 #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;)
121 #define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help;
123 #define LONG_PAYMENT_ID_SUPPORT_CHECK() \
125 if (!m_long_payment_id_support) { \
126 fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \
127 fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \
128 fail_msg_writer() << tr("Use --long-payment-id-support-bad-for-privacy if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \
140 const std::array<const char* const, 5> allowed_priority_strings = {{
"default",
"unimportant",
"normal",
"elevated",
"priority"}};
154 const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {
"allow-mismatched-daemon-version",
sw::tr(
"Allow communicating with a daemon that uses a different RPC version"),
false};
161 const command_line::arg_descriptor<bool> arg_long_payment_id_support = {
"long-payment-id-support-bad-for-privacy",
sw::tr(
"Support obsolete long (unencrypted) payment ids (using them harms your privacy)"),
true};
162 const command_line::arg_descriptor<uint64_t> fallback_to_pow_checkpoint_height = {
"fallback-to-pow-checkpoint-height",
tools::wallet2::tr(
"Warning: This is to set the height for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), 0,
true};
163 const command_line::arg_descriptor<std::string> fallback_to_pow_checkpoint_hash = {
"fallback-to-pow-checkpoint-hash",
tools::wallet2::tr(
"Warning: This is to set the hash for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "),
"",
true};
169 const char* USAGE_START_MINING(
"start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
170 const char* USAGE_SET_DAEMON(
"set_daemon <host>[:<port>] [trusted|untrusted]");
171 const char* USAGE_SHOW_BALANCE(
"balance [detail]");
172 const char* USAGE_INCOMING_TRANSFERS(
"incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
173 const char* USAGE_PAYMENTS(
"payments <PID_1> [<PID_2> ... <PID_N>]");
174 const char* USAGE_PAYMENT_ID(
"payment_id");
175 const char* USAGE_TRANSFER(
"transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
176 const char* USAGE_LOCKED_TRANSFER(
"locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
177 const char* USAGE_LOCKED_SWEEP_ALL(
"locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
178 const char* USAGE_SWEEP_ALL(
"sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
179 const char* USAGE_SWEEP_BELOW(
"sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
180 const char* USAGE_SWEEP_SINGLE(
"sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
181 const char* USAGE_DONATE(
"donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]");
182 const char* USAGE_SIGN_TRANSFER(
"sign_transfer [export_raw]");
183 const char* USAGE_SET_LOG(
"set_log <level>|{+,-,}<categories>");
184 const char* USAGE_ACCOUNT(
"account\n"
185 " account new <label text with white spaces allowed>\n"
186 " account switch <index> \n"
187 " account label <index> <label text with white spaces allowed>\n"
188 " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
189 " account untag <account_index_1> [<account_index_2> ...]\n"
190 " account tag_description <tag_name> <description>");
191 const char* USAGE_ADDRESS(
"address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]");
192 const char* USAGE_INTEGRATED_ADDRESS(
"integrated_address [<payment_id> | <address>]");
193 const char* USAGE_ADDRESS_BOOK(
"address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
194 const char* USAGE_SET_VARIABLE(
"set <option> [<value>]");
195 const char* USAGE_GET_TX_KEY(
"get_tx_key <txid>");
196 const char* USAGE_SET_TX_KEY(
"set_tx_key <txid> <tx_key>");
197 const char* USAGE_CHECK_TX_KEY(
"check_tx_key <txid> <txkey> <address>");
198 const char* USAGE_GET_TX_PROOF(
"get_tx_proof <txid> <address> [<message>]");
199 const char* USAGE_CHECK_TX_PROOF(
"check_tx_proof <txid> <address> <signature_file> [<message>]");
200 const char* USAGE_GET_SPEND_PROOF(
"get_spend_proof <txid> [<message>]");
201 const char* USAGE_CHECK_SPEND_PROOF(
"check_spend_proof <txid> <signature_file> [<message>]");
202 const char* USAGE_GET_RESERVE_PROOF(
"get_reserve_proof (all|<amount>) [<message>]");
203 const char* USAGE_CHECK_RESERVE_PROOF(
"check_reserve_proof <address> <signature_file> [<message>]");
204 const char* USAGE_SHOW_TRANSFERS(
"show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
205 const char* USAGE_UNSPENT_OUTPUTS(
"unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]");
206 const char* USAGE_RESCAN_BC(
"rescan_bc [hard|soft|keep_ki] [start_height=0]");
207 const char* USAGE_SET_TX_NOTE(
"set_tx_note <txid> [free text note]");
208 const char* USAGE_GET_TX_NOTE(
"get_tx_note <txid>");
209 const char* USAGE_GET_DESCRIPTION(
"get_description");
210 const char* USAGE_SET_DESCRIPTION(
"set_description [free text note]");
211 const char* USAGE_SIGN(
"sign <filename>");
212 const char* USAGE_VERIFY(
"verify <filename> <address> <signature>");
213 const char* USAGE_EXPORT_KEY_IMAGES(
"export_key_images <filename>");
214 const char* USAGE_IMPORT_KEY_IMAGES(
"import_key_images <filename>");
215 const char* USAGE_HW_KEY_IMAGES_SYNC(
"hw_key_images_sync");
216 const char* USAGE_HW_RECONNECT(
"hw_reconnect");
217 const char* USAGE_EXPORT_OUTPUTS(
"export_outputs <filename>");
218 const char* USAGE_IMPORT_OUTPUTS(
"import_outputs <filename>");
219 const char* USAGE_SHOW_TRANSFER(
"show_transfer <txid>");
220 const char* USAGE_MAKE_MULTISIG(
"make_multisig <threshold> <string1> [<string>...]");
221 const char* USAGE_FINALIZE_MULTISIG(
"finalize_multisig <string> [<string>...]");
222 const char* USAGE_EXCHANGE_MULTISIG_KEYS(
"exchange_multisig_keys <string> [<string>...]");
223 const char* USAGE_EXPORT_MULTISIG_INFO(
"export_multisig_info <filename>");
224 const char* USAGE_IMPORT_MULTISIG_INFO(
"import_multisig_info <filename> [<filename>...]");
225 const char* USAGE_SIGN_MULTISIG(
"sign_multisig <filename>");
226 const char* USAGE_SUBMIT_MULTISIG(
"submit_multisig <filename>");
227 const char* USAGE_EXPORT_RAW_MULTISIG_TX(
"export_raw_multisig_tx <filename>");
228 const char* USAGE_MMS(
"mms [<subcommand> [<subcommand_parameters>]]");
229 const char* USAGE_MMS_INIT(
"mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
230 const char* USAGE_MMS_INFO(
"mms info");
231 const char* USAGE_MMS_SIGNER(
"mms signer [<number> <label> [<transport_address> [<etn_address>]]]");
232 const char* USAGE_MMS_LIST(
"mms list");
233 const char* USAGE_MMS_NEXT(
"mms next [sync]");
234 const char* USAGE_MMS_SYNC(
"mms sync");
235 const char* USAGE_MMS_TRANSFER(
"mms transfer <transfer_command_arguments>");
236 const char* USAGE_MMS_DELETE(
"mms delete (<message_id> | all)");
237 const char* USAGE_MMS_SEND(
"mms send [<message_id>]");
238 const char* USAGE_MMS_RECEIVE(
"mms receive");
239 const char* USAGE_MMS_EXPORT(
"mms export <message_id>");
240 const char* USAGE_MMS_NOTE(
"mms note [<label> <text>]");
241 const char* USAGE_MMS_SHOW(
"mms show <message_id>");
242 const char* USAGE_MMS_SET(
"mms set <option_name> [<option_value>]");
243 const char* USAGE_MMS_SEND_SIGNER_CONFIG(
"mms send_signer_config");
244 const char* USAGE_MMS_START_AUTO_CONFIG(
"mms start_auto_config [<label> <label> ...]");
245 const char* USAGE_MMS_STOP_AUTO_CONFIG(
"mms stop_auto_config");
246 const char* USAGE_MMS_AUTO_CONFIG(
"mms auto_config <auto_config_token>");
247 const char* USAGE_PRINT_RING(
"print_ring <key_image> | <txid>");
248 const char* USAGE_SET_RING(
"set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )");
249 const char* USAGE_UNSET_RING(
"unset_ring <txid> | ( <key_image> [<key_image>...] )");
250 const char* USAGE_SAVE_KNOWN_RINGS(
"save_known_rings");
251 const char* USAGE_MARK_OUTPUT_SPENT(
"mark_output_spent <amount>/<offset> | <filename> [add]");
252 const char* USAGE_MARK_OUTPUT_UNSPENT(
"mark_output_unspent <amount>/<offset>");
253 const char* USAGE_IS_OUTPUT_SPENT(
"is_output_spent <amount>/<offset>");
254 const char* USAGE_FREEZE(
"freeze <key_image>");
255 const char* USAGE_THAW(
"thaw <key_image>");
256 const char* USAGE_FROZEN(
"frozen <key_image>");
257 const char* USAGE_NET_STATS(
"net_stats");
258 const char* USAGE_WELCOME(
"welcome");
259 const char* USAGE_VERSION(
"version");
260 const char* USAGE_HELP(
"help [<command>]");
269 std::cout <<
" (Y/Yes/N/No)";
270 std::cout <<
": " << std::flush;
274 buf = tools::input_line_win();
276 std::getline(std::cin,
buf);
290 MERROR(
"Failed to read secure line");
300 boost::optional<tools::password_container> password_prompter(
const char *prompt,
bool verify)
310 return pwd_container;
313 boost::optional<tools::password_container> default_password_prompter(
bool verify)
315 return password_prompter(verify ?
sw::tr(
"Enter a new password for the wallet") :
sw::tr(
"Wallet password"), verify);
325 err =
sw::tr(
"daemon is busy. Please try again later.");
334 err =
sw::tr(
"possibly lost connection to daemon");
354 bool parse_bool(
const std::string& s,
bool& result)
367 boost::algorithm::is_iequal ignore_case{};
368 if (boost::algorithm::equals(
"true", s, ignore_case) || boost::algorithm::equals(
simple_wallet::tr(
"true"), s, ignore_case))
373 if (boost::algorithm::equals(
"false", s, ignore_case) || boost::algorithm::equals(
simple_wallet::tr(
"false"), s, ignore_case))
382 template <
typename F>
386 if (parse_bool(s, r))
402 } refresh_type_names[] =
413 for (
size_t n = 0; n <
sizeof(refresh_type_names) /
sizeof(refresh_type_names[0]); ++n)
415 if (s == refresh_type_names[n].
name)
417 refresh_type = refresh_type_names[n].refresh_type;
427 for (
size_t n = 0; n <
sizeof(refresh_type_names) /
sizeof(refresh_type_names[0]); ++n)
429 if (type == refresh_type_names[n].refresh_type)
430 return refresh_type_names[n].name;
437 return boost::lexical_cast<std::string>(
version >> 16) +
"." + boost::lexical_cast<std::string>(
version & 0xffff);
440 std::string oa_prompter(
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)
442 if (addresses.empty())
449 dnssec_str =
sw::tr(
"DNSSEC validation passed");
453 dnssec_str =
sw::tr(
"WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
455 std::stringstream prompt;
456 prompt <<
sw::tr(
"For URL: ") << url
457 <<
", " << dnssec_str << std::endl
458 <<
sw::tr(
" Electroneum Address = ") << addresses[0]
463 std::string confirm_dns_ok = input_line(prompt.str(),
true);
470 std::cout <<
sw::tr(
"you have cancelled the transfer request") << std::endl;
476 bool parse_subaddress_indices(
const std::string& arg, std::set<uint32_t>& subaddr_indices)
478 subaddr_indices.clear();
480 if (arg.substr(0, 6) !=
"index=")
482 std::string subaddr_indices_str_unsplit = arg.substr(6, arg.size() - 6);
483 std::vector<std::string> subaddr_indices_str;
484 boost::split(subaddr_indices_str, subaddr_indices_str_unsplit, boost::is_any_of(
","));
486 for (
const auto& subaddr_index_str : subaddr_indices_str)
492 subaddr_indices.clear();
495 subaddr_indices.insert(subaddr_index);
508 void handle_transfer_exception(
const std::exception_ptr &e,
bool trusted_daemon)
510 bool warn_of_possible_attack = !trusted_daemon;
513 std::rethrow_exception(e);
534 LOG_PRINT_L0(boost::format(
"not enough ETN to transfer, available only %s, sent amount %s") %
538 warn_of_possible_attack =
false;
542 LOG_PRINT_L0(boost::format(
"not enough ETN to transfer, available only %s, sent amount %s") %
546 warn_of_possible_attack =
false;
550 LOG_PRINT_L0(boost::format(
"not enough ETN to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
555 fail_msg_writer() <<
sw::tr(
"Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more ETN than the unlocked balance, or not leaving enough for fees");
556 warn_of_possible_attack =
false;
561 writer <<
sw::tr(
"not enough outputs for specified ring size") <<
" = " << (e.
mixin_count() + 1) <<
":";
562 for (std::pair<uint64_t, uint64_t> outs_for_amount : e.
scanty_outs())
564 writer <<
"\n" <<
sw::tr(
"output amount") <<
" = " <<
print_etn(outs_for_amount.first) <<
", " <<
sw::tr(
"found outputs to use") <<
" = " << outs_for_amount.second;
566 writer <<
sw::tr(
"Please use sweep_unmixable.");
571 warn_of_possible_attack =
false;
583 warn_of_possible_attack =
false;
588 warn_of_possible_attack =
false;
593 warn_of_possible_attack =
false;
604 warn_of_possible_attack =
false;
611 catch (
const std::exception& e)
613 LOG_ERROR(
"unexpected error: " << e.what());
617 if (warn_of_possible_attack)
618 fail_msg_writer() <<
sw::tr(
"There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
621 bool check_file_overwrite(
const std::string &filename)
623 boost::system::error_code errcode;
624 if (boost::filesystem::exists(filename, errcode))
626 if (boost::ends_with(filename,
".keys"))
628 fail_msg_writer() << boost::format(
sw::tr(
"File %s likely stores wallet private keys! Use a different file name.")) % filename;
631 return command_line::is_yes(input_line((boost::format(
sw::tr(
"File %s already exists. Are you sure to overwrite it?")) % filename).str(),
true));
638 static constexpr
const char hex[] =
u8"0123456789abcdef";
640 for (
size_t i = 0, sz =
sizeof(k); i < sz; ++i)
642 putchar(hex[*ptr >> 4]);
643 putchar(hex[*ptr & 15]);
651 auto priority_pos = std::find(
652 allowed_priority_strings.begin(),
653 allowed_priority_strings.end(),
655 if(priority_pos != allowed_priority_strings.end()) {
656 priority = std::distance(allowed_priority_strings.begin(), priority_pos);
665 for (
size_t n = 0; n < allowed_priority_strings.size(); ++n)
669 s += allowed_priority_strings[n];
676 std::stringstream ss;
677 ss <<
tr(
"Commands: ") <<
ENDL;
679 boost::replace_all(usage,
"\n",
"\n ");
680 usage.insert(0,
" ");
685 std::string simple_wallet::get_command_usage(
const std::vector<std::string> &args)
687 std::pair<std::string, std::string> documentation = m_cmd_binder.get_documentation(args);
688 std::stringstream ss;
689 if(documentation.first.empty())
691 ss <<
tr(
"Unknown command: ") << args.front();
695 std::string usage = documentation.second.empty() ? args.front() : documentation.first;
696 std::string description = documentation.second.empty() ? documentation.first : documentation.second;
697 usage.insert(0,
" ");
699 boost::replace_all(description,
"\n",
"\n ");
700 description.insert(0,
" ");
701 ss <<
tr(
"Command description: ") <<
ENDL << description <<
ENDL;
706 bool simple_wallet::viewkey(
const std::vector<std::string> &args)
710 if (m_wallet->key_on_device()) {
711 std::cout <<
"secret: On device. Not available" << std::endl;
715 print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
718 std::cout <<
"public: " <<
string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
725 if (m_wallet->watch_only())
732 if (m_wallet->key_on_device()) {
733 std::cout <<
"secret: On device. Not available" << std::endl;
737 print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
740 std::cout <<
"public: " <<
string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
745 bool simple_wallet::print_seed(
bool encrypted)
749 bool ready, multisig;
751 if (m_wallet->key_on_device())
756 if (m_wallet->watch_only())
762 multisig = m_wallet->multisig(&ready);
774 if (!multisig && !m_wallet->is_deterministic())
783 auto pwd_container = password_prompter(
tr(
"Enter optional seed offset passphrase, empty to see raw seed"),
true);
784 if (std::cin.eof() || !pwd_container)
786 seed_pass = pwd_container->password();
790 success = m_wallet->get_multisig_seed(seed, seed_pass);
791 else if (m_wallet->is_deterministic())
792 success = m_wallet->get_seed(seed, seed_pass);
805 bool simple_wallet::seed(
const std::vector<std::string> &args)
807 return print_seed(
false);
810 bool simple_wallet::encrypted_seed(
const std::vector<std::string> &args)
812 return print_seed(
true);
815 bool simple_wallet::seed_set_language(
const std::vector<std::string> &args)
817 if (m_wallet->key_on_device())
822 if (m_wallet->multisig())
827 if (m_wallet->watch_only())
837 if (!m_wallet->is_deterministic())
846 pwd_container = get_and_verify_password();
847 if (pwd_container == boost::none)
853 password = pwd_container->password();
856 std::string mnemonic_language = get_mnemonic_language();
857 if (mnemonic_language.empty())
860 m_wallet->set_seed_language(
std::move(mnemonic_language));
861 m_wallet->rewrite(m_wallet_file, password);
865 bool simple_wallet::change_password(
const std::vector<std::string> &args)
867 const auto orig_pwd_container = get_and_verify_password();
869 if(orig_pwd_container == boost::none)
876 const auto pwd_container = default_password_prompter(
true);
882 m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
893 bool simple_wallet::payment_id(
const std::vector<std::string> &args)
903 payment_id = crypto::rand<crypto::hash>();
908 bool simple_wallet::print_fee_info(
const std::vector<std::string> &args)
910 if (!try_connect_to_daemon())
913 const uint64_t base_fee = m_wallet->get_base_fee();
914 const char *base = per_byte ?
"byte" :
"kB";
915 const uint64_t typical_size = per_byte ? 2500 : 13;
916 const uint64_t size_granularity = per_byte ? 1 : 1024;
919 std::vector<uint64_t> fees;
920 for (
uint32_t priority = 1; priority <= 4; ++priority)
922 uint64_t mult = m_wallet->get_fee_multiplier(priority);
923 fees.push_back(base_fee * typical_size * mult);
925 std::vector<std::pair<uint64_t, uint64_t>>
blocks;
928 uint64_t base_size = typical_size * size_granularity;
929 blocks = m_wallet->estimate_backlog(base_size, base_size + size_granularity - 1, fees);
931 catch (
const std::exception &e)
933 fail_msg_writer() <<
tr(
"Error: failed to estimate backlog array size: ") << e.what();
942 for (
uint32_t priority = 1; priority <= 4; ++priority)
949 if (priority == m_wallet->get_default_priority() || (m_wallet->get_default_priority() == 0 && priority == 2))
950 msg =
tr(
" (current)");
952 if (nblocks_high == nblocks_low)
953 message_writer() << (boost::format(
tr(
"%u block (%u minutes) backlog at priority %u%s")) % nblocks_low % minutes_low % priority % msg).str();
955 message_writer() << (boost::format(
tr(
"%u to %u block (%u to %u minutes) backlog at priority %u")) % nblocks_low % nblocks_high % minutes_low % minutes_high % priority).str();
958 message_writer() <<
tr(
"No backlog at priority ") << priority;
963 bool simple_wallet::prepare_multisig(
const std::vector<std::string> &args)
965 prepare_multisig_main(args,
false);
969 bool simple_wallet::prepare_multisig_main(
const std::vector<std::string> &args,
bool called_by_mms)
971 if (m_wallet->key_on_device())
976 if (m_wallet->multisig())
981 if (m_wallet->watch_only())
987 if(m_wallet->get_num_transfer_details())
989 fail_msg_writer() <<
tr(
"This wallet has been used before, please use a new wallet to create a multisig wallet");
995 std::string multisig_info = m_wallet->get_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 ");
1008 bool simple_wallet::make_multisig(
const std::vector<std::string> &args)
1010 make_multisig_main(args,
false);
1014 bool simple_wallet::make_multisig_main(
const std::vector<std::string> &args,
bool called_by_mms)
1016 if (m_wallet->key_on_device())
1021 if (m_wallet->multisig())
1026 if (m_wallet->watch_only())
1032 if(m_wallet->get_num_transfer_details())
1034 fail_msg_writer() <<
tr(
"This wallet has been used before, please use a new wallet to create a multisig wallet");
1038 if (args.size() < 2)
1052 const auto orig_pwd_container = get_and_verify_password();
1053 if(orig_pwd_container == boost::none)
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())
1070 success_msg_writer() <<
tr(
"Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
1078 catch (
const std::exception &e)
1085 if (!m_wallet->multisig(NULL, &
threshold, &total))
1091 << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
1096 bool simple_wallet::finalize_multisig(
const std::vector<std::string> &args)
1099 if (m_wallet->key_on_device())
1105 const auto pwd_container = get_and_verify_password();
1106 if(pwd_container == boost::none)
1112 if (!m_wallet->multisig(&ready))
1125 if (args.size() < 2)
1133 if (!m_wallet->finalize_multisig(pwd_container->password(), args))
1139 catch (
const std::exception &e)
1148 bool simple_wallet::exchange_multisig_keys(
const std::vector<std::string> &args)
1150 exchange_multisig_keys_main(args,
false);
1154 bool simple_wallet::exchange_multisig_keys_main(
const std::vector<std::string> &args,
bool called_by_mms) {
1156 if (m_wallet->key_on_device())
1161 if (!m_wallet->multisig(&ready))
1172 const auto orig_pwd_container = get_and_verify_password();
1173 if(orig_pwd_container == boost::none)
1179 if (args.size() < 2)
1187 std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
1188 if (!multisig_extra_info.empty())
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");
1200 m_wallet->multisig(NULL, &
threshold, &total);
1202 success_msg_writer() <<
tr(
"Multisig address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
1205 catch (
const std::exception &e)
1207 fail_msg_writer() <<
tr(
"Failed to perform multisig keys exchange: ") << e.what();
1214 bool simple_wallet::export_multisig(
const std::vector<std::string> &args)
1216 export_multisig_main(args,
false);
1220 bool simple_wallet::export_multisig_main(
const std::vector<std::string> &args,
bool called_by_mms)
1223 if (m_wallet->key_on_device())
1228 if (!m_wallet->multisig(&ready))
1238 if (args.size() != 1)
1245 if (!called_by_mms && m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
1268 catch (
const std::exception &e)
1270 LOG_ERROR(
"Error exporting multisig info: " << e.what());
1279 bool simple_wallet::import_multisig(
const std::vector<std::string> &args)
1281 import_multisig_main(args,
false);
1285 bool simple_wallet::import_multisig_main(
const std::vector<std::string> &args,
bool called_by_mms)
1289 if (m_wallet->key_on_device())
1294 if (!m_wallet->multisig(&ready, &
threshold, &total))
1310 std::vector<cryptonote::blobdata>
info;
1311 for (
size_t n = 0; n < args.size(); ++n)
1315 info.push_back(args[n]);
1336 m_in_manual_refresh.store(
true, std::memory_order_relaxed);
1338 size_t n_outputs = m_wallet->import_multisig(
info);
1340 std::cout <<
"\r \r";
1343 catch (
const std::exception &e)
1348 if (m_wallet->is_trusted_daemon())
1352 m_wallet->rescan_spent();
1354 catch (
const std::exception &e)
1356 message_writer() <<
tr(
"Failed to update spent status after importing multisig info: ") << e.what();
1362 message_writer() <<
tr(
"Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run \"rescan_spent\"");
1374 bool simple_wallet::sign_multisig(
const std::vector<std::string> &args)
1376 sign_multisig_main(args,
false);
1380 bool simple_wallet::sign_multisig_main(
const std::vector<std::string> &args,
bool called_by_mms)
1383 if (m_wallet->key_on_device())
1388 if(!m_wallet->multisig(&ready))
1398 if (args.size() != 1)
1407 std::vector<crypto::hash> txids;
1418 r = m_wallet->sign_multisig_tx(exported_txs, txids);
1422 ciphertext = m_wallet->save_multisig_tx(exported_txs);
1423 if (ciphertext.empty())
1435 get_message_store().process_wallet_created_data(get_multisig_wallet_state(),
message_type, ciphertext);
1459 catch (
const std::exception &e)
1471 << signers_needed <<
" more signer(s) needed";
1477 for (
const auto &txid: txids)
1479 if (!txids_as_text.empty())
1480 txids_as_text += (
", ");
1483 success_msg_writer(
true) <<
tr(
"Transaction successfully signed to file ") << filename <<
", txid " << txids_as_text;
1489 bool simple_wallet::submit_multisig(
const std::vector<std::string> &args)
1491 submit_multisig_main(args,
false);
1495 bool simple_wallet::submit_multisig_main(
const std::vector<std::string> &args,
bool called_by_mms)
1499 if (m_wallet->key_on_device())
1504 if (!m_wallet->multisig(&ready, &
threshold))
1514 if (args.size() != 1)
1520 if (!try_connect_to_daemon())
1549 fail_msg_writer() << (boost::format(
tr(
"Multisig transaction signed by only %u signers, needs %u more signatures"))
1555 for (
auto &ptx: txs.
m_ptx)
1557 m_wallet->commit_tx(ptx);
1559 <<
tr(
"You can check its status by using the `show_transfers` command.");
1562 catch (
const std::exception &e)
1564 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
1576 bool simple_wallet::export_raw_multisig(
const std::vector<std::string> &args)
1580 if (m_wallet->key_on_device())
1585 if (!m_wallet->multisig(&ready, &
threshold))
1595 if (args.size() != 1)
1602 if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
1618 fail_msg_writer() << (boost::format(
tr(
"Multisig transaction signed by only %u signers, needs %u more signatures"))
1625 for (
auto &ptx: txs.
m_ptx)
1629 if (!filenames.empty())
1631 filenames += filename;
1634 fail_msg_writer() <<
tr(
"Failed to export multisig transaction to file ") << filename;
1640 catch (
const std::exception& e)
1642 LOG_ERROR(
"unexpected error: " << e.what());
1654 bool simple_wallet::print_ring(
const std::vector<std::string> &args)
1658 if (args.size() != 1)
1676 std::vector<uint64_t> ring;
1677 std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings;
1680 if (m_wallet->get_ring(
key_image, ring))
1682 else if (!m_wallet->get_rings(txid, rings))
1688 for (
const auto &ring: rings)
1690 std::stringstream str;
1691 for (
const auto &x: ring.second)
1697 catch (
const std::exception &e)
1705 bool simple_wallet::set_ring(
const std::vector<std::string> &args)
1710 if (args.size() == 1)
1719 std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(),
"r"));
1722 while (!feof(f.get()))
1724 if (!fgets(str,
sizeof(str), f.get()))
1726 const size_t len = strlen(str);
1727 if (len > 0 && str[len - 1] ==
'\n')
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);
1739 key_image_str[64] = 0;
1747 if (read == read_after_key_image+8 || (strcmp(type_str,
"absolute") && strcmp(type_str,
"relative")))
1749 fail_msg_writer() <<
tr(
"Invalid ring type, expected relative or abosolute: ") << str;
1752 bool relative = !strcmp(type_str,
"relative");
1753 if (read < 0 || (
size_t)read > strlen(str))
1759 std::vector<uint64_t> ring;
1760 const char *ptr = str + read;
1763 unsigned long offset;
1764 int elements = sscanf(ptr,
"%lu %n", &offset, &read);
1765 if (elements == 0 || read <= 0 || (
size_t)read > strlen(str))
1771 ring.push_back(offset);
1783 for (
size_t n = 1; n < ring.size(); ++n)
1795 for (
size_t n = 1; n < ring.size(); ++n)
1797 if (ring[n] <= ring[n-1])
1807 if (!m_wallet->set_ring(
key_image, ring, relative))
1815 if (args.size() < 3)
1828 if (args[1] ==
"absolute")
1832 else if (args[1] ==
"relative")
1842 std::vector<uint64_t> ring;
1843 for (
size_t n = 2; n < args.size(); ++n)
1845 ring.resize(ring.size() + 1);
1848 fail_msg_writer() <<
tr(
"invalid index: must be a strictly positive unsigned integer");
1853 if (ring.size() > 1 && !ring.back())
1855 fail_msg_writer() <<
tr(
"invalid index: must be a strictly positive unsigned integer");
1861 if (out > std::numeric_limits<uint64_t>::max() - sum)
1871 if (ring.size() > 1 && ring[ring.size() - 2] >= ring[ring.size() - 1])
1873 fail_msg_writer() <<
tr(
"invalid index: indices should be in strictly ascending order");
1878 if (!m_wallet->set_ring(
key_image, ring, relative))
1887 bool simple_wallet::unset_ring(
const std::vector<std::string> &args)
1890 std::vector<crypto::key_image> key_images;
1892 if (args.size() < 1)
1898 key_images.resize(args.size());
1899 for (
size_t i = 0; i < args.size(); ++i)
1908 memcpy(&txid, &key_images[0],
sizeof(txid));
1910 if (!m_wallet->unset_ring(key_images) && !m_wallet->unset_ring(txid))
1919 bool simple_wallet::blackball(
const std::vector<std::string> &args)
1921 uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
1922 if (args.size() == 0)
1930 if (sscanf(args[0].c_str(),
"%" PRIu64 "/%" PRIu64, &amount, &offset) == 2)
1932 m_wallet->blackball_output(std::make_pair(amount, offset));
1936 std::vector<std::pair<uint64_t, uint64_t>> outputs;
1939 std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(),
"r"));
1942 while (!feof(f.get()))
1944 if (!fgets(str,
sizeof(str), f.get()))
1946 const size_t len = strlen(str);
1947 if (len > 0 && str[len - 1] ==
'\n')
1951 if (sscanf(str,
"@%" PRIu64, &amount) == 1)
1955 if (amount == std::numeric_limits<uint64_t>::max())
1960 if (sscanf(str,
"%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits<uint64_t>::max() - offset)
1962 while (num_offsets--)
1963 outputs.push_back(std::make_pair(amount, offset++));
1965 else if (sscanf(str,
"%" PRIu64, &offset) == 1)
1967 outputs.push_back(std::make_pair(amount, offset));
1977 if (args.size() > 1)
1979 if (args[1] !=
"add")
1986 m_wallet->set_blackballed_outputs(outputs, add);
2000 catch (
const std::exception &e)
2008 bool simple_wallet::unblackball(
const std::vector<std::string> &args)
2010 std::pair<uint64_t, uint64_t> output;
2011 if (args.size() != 1)
2017 if (sscanf(args[0].c_str(),
"%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2)
2025 m_wallet->unblackball_output(output);
2027 catch (
const std::exception &e)
2035 bool simple_wallet::blackballed(
const std::vector<std::string> &args)
2037 std::pair<uint64_t, uint64_t> output;
2038 if (args.size() != 1)
2044 if (sscanf(args[0].c_str(),
"%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2)
2052 if (m_wallet->is_output_blackballed(output))
2053 message_writer() <<
tr(
"Spent: ") << output.first <<
"/" << output.second;
2055 message_writer() <<
tr(
"Not spent: ") << output.first <<
"/" << output.second;
2057 catch (
const std::exception &e)
2059 fail_msg_writer() <<
tr(
"Failed to check whether output is spent: ") << e.what();
2065 bool simple_wallet::save_known_rings(
const std::vector<std::string> &args)
2070 m_wallet->find_and_save_rings();
2072 catch (
const std::exception &e)
2079 bool simple_wallet::freeze_thaw(
const std::vector<std::string> &args,
bool freeze)
2083 fail_msg_writer() << boost::format(
tr(
"usage: %s <key_image>|<pubkey>")) % (freeze ?
"freeze" :
"thaw");
2095 m_wallet->freeze(ki);
2099 catch (
const std::exception &e)
2108 bool simple_wallet::freeze(
const std::vector<std::string> &args)
2110 return freeze_thaw(args,
true);
2113 bool simple_wallet::thaw(
const std::vector<std::string> &args)
2115 return freeze_thaw(args,
false);
2118 bool simple_wallet::frozen(
const std::vector<std::string> &args)
2122 size_t ntd = m_wallet->get_num_transfer_details();
2123 for (
size_t i = 0; i < ntd; ++i)
2125 if (!m_wallet->frozen(i))
2139 if (m_wallet->frozen(ki))
2140 message_writer() <<
tr(
"Frozen: ") << ki;
2142 message_writer() <<
tr(
"Not frozen: ") << ki;
2147 bool simple_wallet::net_stats(
const std::vector<std::string> &args)
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");
2154 bool simple_wallet::welcome(
const std::vector<std::string> &args)
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/");
2170 std::vector<std::string> tx_aux;
2174 m_wallet->cold_sign_tx(ptx_vector, exported_txs, dsts_info, tx_aux);
2176 if (accept_func && !accept_func(exported_txs))
2178 MERROR(
"Transactions rejected by callback");
2183 m_wallet->cold_tx_aux_import(exported_txs.
ptx, tx_aux);
2186 return m_wallet->import_key_images(exported_txs, 0,
true);
2189 bool simple_wallet::set_always_confirm_transfers(
const std::vector<std::string> &args)
2191 const auto pwd_container = get_and_verify_password();
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());
2202 bool simple_wallet::set_print_ring_members(
const std::vector<std::string> &args)
2204 const auto pwd_container = get_and_verify_password();
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());
2215 bool simple_wallet::set_store_tx_info(
const std::vector<std::string> &args)
2217 if (m_wallet->watch_only())
2223 const auto pwd_container = get_and_verify_password();
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());
2234 bool simple_wallet::set_default_ring_size(
const std::vector<std::string> &args)
2236 if (m_wallet->watch_only())
2243 fail_msg_writer() <<
tr(
"Cannot set default ring size: ring size is enforced at 1.");
2249 if (strchr(args[1].c_str(),
'-'))
2254 uint32_t ring_size = boost::lexical_cast<uint32_t>(args[1]);
2262 message_writer() <<
tr(
"WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
2264 message_writer() <<
tr(
"WARNING: from v8, ring size will be fixed and this setting will be ignored.");
2266 const auto pwd_container = get_and_verify_password();
2269 m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0);
2270 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2274 catch(
const boost::bad_lexical_cast &)
2286 bool simple_wallet::set_default_priority(
const std::vector<std::string> &args)
2291 if (strchr(args[1].c_str(),
'-'))
2303 for (
size_t n = 0; n < allowed_priority_strings.size(); ++n)
2305 if (allowed_priority_strings[n] == args[1])
2313 priority = boost::lexical_cast<int>(args[1]);
2314 if (priority < 1 || priority > 4)
2322 const auto pwd_container = get_and_verify_password();
2325 m_wallet->set_default_priority(priority);
2326 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2330 catch(
const boost::bad_lexical_cast &)
2342 bool simple_wallet::set_auto_refresh(
const std::vector<std::string> &args)
2344 const auto pwd_container = get_and_verify_password();
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();
2355 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2361 bool simple_wallet::set_refresh_type(
const std::vector<std::string> &args)
2364 if (!parse_refresh_type(args[1], refresh_type))
2369 const auto pwd_container = get_and_verify_password();
2372 m_wallet->set_refresh_type(refresh_type);
2373 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2378 bool simple_wallet::set_confirm_missing_payment_id(
const std::vector<std::string> &args)
2382 const auto pwd_container = get_and_verify_password();
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());
2393 bool simple_wallet::set_ask_password(
const std::vector<std::string> &args)
2395 const auto pwd_container = get_and_verify_password();
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")
2407 fail_msg_writer() <<
tr(
"invalid argument: must be either 0/never, 1/action, or 2/encrypt/decrypt");
2412 if (!m_wallet->watch_only())
2415 m_wallet->decrypt_keys(pwd_container->password());
2417 m_wallet->encrypt_keys(pwd_container->password());
2419 m_wallet->ask_password(ask);
2420 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2425 bool simple_wallet::set_unit(
const std::vector<std::string> &args)
2430 if (
unit ==
"electroneum")
2432 else if (
unit ==
"ecent")
2440 const auto pwd_container = get_and_verify_password();
2444 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2449 bool simple_wallet::set_min_output_count(
const std::vector<std::string> &args)
2458 const auto pwd_container = get_and_verify_password();
2461 m_wallet->set_min_output_count(
count);
2462 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2467 bool simple_wallet::set_min_output_value(
const std::vector<std::string> &args)
2476 const auto pwd_container = get_and_verify_password();
2479 m_wallet->set_min_output_value(
value);
2480 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2485 bool simple_wallet::set_merge_destinations(
const std::vector<std::string> &args)
2487 const auto pwd_container = get_and_verify_password();
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());
2498 bool simple_wallet::set_confirm_backlog(
const std::vector<std::string> &args)
2500 const auto pwd_container = get_and_verify_password();
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());
2511 bool simple_wallet::set_confirm_backlog_threshold(
const std::vector<std::string> &args)
2520 const auto pwd_container = get_and_verify_password();
2523 m_wallet->set_confirm_backlog_threshold(
threshold);
2524 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2529 bool simple_wallet::set_confirm_export_overwrite(
const std::vector<std::string> &args)
2531 const auto pwd_container = get_and_verify_password();
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());
2542 bool simple_wallet::set_refresh_from_block_height(
const std::vector<std::string> &args)
2544 const auto pwd_container = get_and_verify_password();
2553 m_wallet->set_refresh_from_block_height(
height);
2554 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2559 bool simple_wallet::set_account_major_offset(
const std::vector<std::string> &args)
2561 const auto pwd_container = get_and_verify_password();
2570 m_wallet->account_major_offset(offset);
2571 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2576 bool simple_wallet::set_auto_low_priority(
const std::vector<std::string> &args)
2578 const auto pwd_container = get_and_verify_password();
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());
2589 bool simple_wallet::set_segregate_pre_fork_outputs(
const std::vector<std::string> &args)
2591 const auto pwd_container = get_and_verify_password();
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());
2602 bool simple_wallet::set_key_reuse_mitigation2(
const std::vector<std::string> &args)
2604 const auto pwd_container = get_and_verify_password();
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());
2615 bool simple_wallet::set_subaddress_lookahead(
const std::vector<std::string> &args)
2617 const auto pwd_container = get_and_verify_password();
2623 m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
2624 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2630 bool simple_wallet::set_segregation_height(
const std::vector<std::string> &args)
2632 const auto pwd_container = get_and_verify_password();
2641 m_wallet->segregation_height(
height);
2642 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2647 bool simple_wallet::set_ignore_fractional_outputs(
const std::vector<std::string> &args)
2649 const auto pwd_container = get_and_verify_password();
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());
2660 bool simple_wallet::set_track_uses(
const std::vector<std::string> &args)
2662 const auto pwd_container = get_and_verify_password();
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());
2673 bool simple_wallet::set_setup_background_mining(
const std::vector<std::string> &args)
2675 const auto pwd_container = get_and_verify_password();
2679 if (args[1] ==
"yes" || args[1] ==
"1")
2681 else if (args[1] ==
"no" || args[1] ==
"0")
2688 m_wallet->setup_background_mining(setup);
2689 m_wallet->rewrite(m_wallet_file, pwd_container->password());
2691 start_background_mining();
2693 stop_background_mining();
2698 bool simple_wallet::set_device_name(
const std::vector<std::string> &args)
2700 const auto pwd_container = get_and_verify_password();
2703 if (args.size() == 0){
2708 m_wallet->device_name(args[0]);
2711 r = m_wallet->reconnect_device();
2716 }
catch(
const std::exception & e){
2717 MWARNING(
"Device reconnect failed: " << e.what());
2725 bool simple_wallet::help(
const std::vector<std::string> &args)
2731 else if ((args.size() == 2) && (args.front() ==
"mms"))
2734 std::vector<std::string> mms_args(1, args.front() +
" " + args.back());
2744 simple_wallet::simple_wallet()
2745 : m_allow_mismatched_daemon_version(
false)
2746 , m_refresh_progress_reporter(*this)
2748 , m_auto_refresh_enabled(
false)
2749 , m_auto_refresh_refreshing(
false)
2750 , m_in_manual_refresh(
false)
2751 , m_current_subaddress_account(0)
2763 boost::bind(&simple_wallet::set_daemon,
this, _1),
2764 tr(USAGE_SET_DAEMON),
2765 tr(
"Set another daemon to connect to."));
2767 boost::bind(&simple_wallet::save_bc,
this, _1),
2768 tr(
"Save the current blockchain data."));
2770 boost::bind(&simple_wallet::refresh,
this, _1),
2771 tr(
"Synchronize the transactions and 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."));
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"
2781 "Amount, Spent(\"T\"|\"F\"), \"frozen\"|\"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] "));
2783 boost::bind(&simple_wallet::show_payments,
this, _1),
2785 tr(
"Show the payments for the given payment IDs."));
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),
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)"));
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)"));
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."));
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."));
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."));
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."));
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."));
2825 boost::bind(&simple_wallet::submit_transfer,
this, _1),
2826 tr(
"Submit a signed transaction from a file."));
2828 boost::bind(&simple_wallet::set_log,
this, _1),
2830 tr(
"Change the current log detail (level must be <0-4>)."));
2832 boost::bind(&simple_wallet::account,
this, _1),
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>."));
2842 boost::bind(&simple_wallet::print_address,
this, _1),
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."));
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"));
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."));
2855 tr(
"Save the wallet data."));
2857 boost::bind(&simple_wallet::save_watch_only,
this, _1),
2858 tr(
"Save a watch-only keys file."));
2860 boost::bind(&simple_wallet::viewkey,
this, _1),
2861 tr(
"Display the private view key."));
2864 tr(
"Display the private spend key."));
2866 boost::bind(&simple_wallet::seed,
this, _1),
2867 tr(
"Display the Electrum-style mnemonic seed"));
2869 boost::bind(&simple_wallet::set_variable,
this, _1),
2870 tr(USAGE_SET_VARIABLE),
2871 tr(
"Available options:\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."));
2918 boost::bind(&simple_wallet::encrypted_seed,
this, _1),
2919 tr(
"Display the encrypted Electrum-style mnemonic seed."));
2921 boost::bind(&simple_wallet::rescan_spent,
this, _1),
2922 tr(
"Rescan the blockchain for spent outputs."));
2925 tr(USAGE_GET_TX_KEY),
2926 tr(
"Get the transaction key (r) for a given <txid>."));
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."));
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>."));
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."));
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."));
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>."));
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>."));
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."));
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>."));
2962 boost::bind(&simple_wallet::show_transfers,
this, _1),
2963 tr(USAGE_SHOW_TRANSFERS),
2965 tr(
"Show the incoming/outgoing transfers within an optional height range.\n\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."));
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."));
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."));
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."));
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>."));
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."));
2994 boost::bind(&simple_wallet::set_description,
this, _1),
2995 tr(USAGE_SET_DESCRIPTION),
2996 tr(
"Set an arbitrary description for the wallet."));
2998 boost::bind(&simple_wallet::get_description,
this, _1),
2999 tr(USAGE_GET_DESCRIPTION),
3000 tr(
"Get the description of the wallet."));
3002 boost::bind(&simple_wallet::status,
this, _1),
3003 tr(
"Show the wallet's status."));
3005 boost::bind(&simple_wallet::wallet_info,
this, _1),
3006 tr(
"Show the wallet's information."));
3008 boost::bind(&simple_wallet::sign,
this, _1),
3010 tr(
"Sign the contents of a file."));
3012 boost::bind(&simple_wallet::verify,
this, _1),
3014 tr(
"Verify a signature on the contents of a file."));
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>."));
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."));
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."));
3028 boost::bind(&simple_wallet::hw_reconnect,
this, _1),
3029 tr(USAGE_HW_RECONNECT),
3030 tr(
"Attempts to reconnect HW wallet."));
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."));
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."));
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."));
3044 boost::bind(&simple_wallet::change_password,
this, _1),
3045 tr(
"Change the wallet's password."));
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."));
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"));
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"));
3067 boost::bind(&simple_wallet::export_multisig,
this, _1),
3068 tr(USAGE_EXPORT_MULTISIG_INFO),
3069 tr(
"Export multisig info for other participants"));
3071 boost::bind(&simple_wallet::import_multisig,
this, _1),
3072 tr(USAGE_IMPORT_MULTISIG_INFO),
3073 tr(
"Import multisig info from other participants"));
3075 boost::bind(&simple_wallet::sign_multisig,
this, _1),
3076 tr(USAGE_SIGN_MULTISIG),
3077 tr(
"Sign a multisig transaction from a file"));
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"));
3087 boost::bind(&simple_wallet::mms,
this, _1),
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>"));
3095 boost::bind(&simple_wallet::mms,
this, _1),
3097 tr(
"Initialize and configure the MMS for M/N = number of required signers/number of authorized signers multisig"));
3099 boost::bind(&simple_wallet::mms,
this, _1),
3101 tr(
"Display current MMS configuration"));
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"));
3107 boost::bind(&simple_wallet::mms,
this, _1),
3109 tr(
"List all messages"));
3111 boost::bind(&simple_wallet::mms,
this, _1),
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"));
3116 boost::bind(&simple_wallet::mms,
this, _1),
3118 tr(
"Force generation of multisig sync info regardless of wallet state, to recover from special situations like \"stale data\" errors"));
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"));
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'"));
3128 boost::bind(&simple_wallet::mms,
this, _1),
3130 tr(
"Send a single message by giving its id, or send all waiting messages"));
3132 boost::bind(&simple_wallet::mms,
this, _1),
3133 tr(USAGE_MMS_RECEIVE),
3134 tr(
"Check right away for new messages to receive"));
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\""));
3140 boost::bind(&simple_wallet::mms,
this, _1),
3142 tr(
"Send a one-line message to an authorized signer, identified by its label, or show any waiting unread notes"));
3144 boost::bind(&simple_wallet::mms,
this, _1),
3146 tr(
"Show detailed info about a single message"));
3148 boost::bind(&simple_wallet::mms,
this, _1),
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"));
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"));
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"));
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"));
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"
3174 "Key Image, \"absolute\", list of rings"));
3176 boost::bind(&simple_wallet::set_ring,
this, _1),
3178 tr(
"Set the ring used for a given key image, so it can be reused in a fork"));
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"));
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"));
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"));
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"));
3196 boost::bind(&simple_wallet::blackballed,
this, _1),
3197 tr(USAGE_IS_OUTPUT_SPENT),
3198 tr(
"Checks whether an output is marked as spent"));
3200 boost::bind(&simple_wallet::freeze,
this, _1),
3202 tr(
"Freeze a single output by key image so it will not be used"));
3204 boost::bind(&simple_wallet::thaw,
this, _1),
3206 tr(
"Thaw a single output by key image so it may be used again"));
3208 boost::bind(&simple_wallet::frozen,
this, _1),
3210 tr(
"Checks whether a given output is currently frozen by key image"));
3212 boost::bind(&simple_wallet::net_stats,
this, _1),
3213 tr(USAGE_NET_STATS),
3214 tr(
"Prints simple network stats"));
3216 boost::bind(&simple_wallet::welcome,
this, _1),
3218 tr(
"Prints basic info about Electroneum for first time users"));
3222 tr(
"Returns version information"));
3224 boost::bind(&simple_wallet::help,
this, _1),
3226 tr(
"Show the help section or the documentation about a <command>."));
3229 bool simple_wallet::set_variable(
const std::vector<std::string> &args)
3233 std::string seed_language = m_wallet->get_seed_language();
3234 if (m_use_english_language_names)
3237 uint32_t priority = m_wallet->get_default_priority();
3238 if (priority < allowed_priority_strings.size())
3239 priority_string = allowed_priority_strings[priority];
3241 switch (m_wallet->ask_password())
3247 std::string setup_background_mining_string =
"invalid";
3248 switch (m_wallet->setup_background_mining())
3255 success_msg_writer() <<
"always-confirm-transfers = " << m_wallet->always_confirm_transfers();
3258 success_msg_writer() <<
"default-ring-size = " << (m_wallet->default_mixin() ? m_wallet->default_mixin() + 1 : 0);
3260 success_msg_writer() <<
"refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type());
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 <<
")";
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();
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;
3278 success_msg_writer() <<
"ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
3282 success_msg_writer() <<
"account-major-offset = " << m_wallet->account_major_offset();
3288 #define CHECK_SIMPLE_VARIABLE(name, f, help) do \
3289 if (args[0] == name) { \
3290 if (args.size() <= 1) \
3292 fail_msg_writer() << "set " << #name << ": " << tr("needs an argument") << " (" << help << ")"; \
3302 if (args[0] ==
"seed")
3304 if (args.size() == 1)
3306 fail_msg_writer() <<
tr(
"set seed: needs an argument. available options: language");
3309 else if (args[1] ==
"language")
3311 seed_set_language(args);
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)"));
3348 bool simple_wallet::set_log(
const std::vector<std::string> &args)
3362 fail_msg_writer() << boost::format(
tr(
"wrong number range, use: %s")) % USAGE_SET_LOG;
3377 bool simple_wallet::ask_wallet_create_if_needed()
3379 LOG_PRINT_L3(
"simple_wallet::ask_wallet_create_if_needed() started");
3382 bool wallet_name_valid =
false;
3383 bool keys_file_exists;
3384 bool wallet_file_exists;
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)")
3396 LOG_ERROR(
"Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
3401 fail_msg_writer() <<
tr(
"Wallet name not valid. Please try again or use Ctrl-C to quit.");
3402 wallet_name_valid =
false;
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);
3411 if((keys_file_exists || wallet_file_exists) && (!m_generate_new.empty() || m_restoring))
3413 fail_msg_writer() <<
tr(
"Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.");
3416 if(wallet_file_exists && keys_file_exists)
3419 m_wallet_file = wallet_path;
3422 else if(!wallet_file_exists && keys_file_exists)
3425 m_wallet_file = wallet_path;
3428 else if(wallet_file_exists && !keys_file_exists)
3430 fail_msg_writer() <<
tr(
"Key file not found. Failed to open wallet: ") <<
"\"" << wallet_path <<
"\". Exiting.";
3433 else if(!wallet_file_exists && !keys_file_exists)
3438 message_writer() <<
tr(
"No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path;
3439 confirm_creation = input_line(
"",
true);
3442 LOG_ERROR(
"Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
3450 m_generate_new = wallet_path;
3455 }
while(!wallet_name_valid);
3457 LOG_ERROR(
"Failed out of do-while loop in ask_wallet_create_if_needed()");
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"));
3471 int space_index = 0;
3472 size_t len = seed.
size();
3473 for (
const char *ptr = seed.
data(); len--; ++ptr)
3477 if (space_index == 15 || space_index == 7)
3492 std::vector<epee::wipeable_string> seed;
3495 return seed.size() < 24;
3500 if (heightstr.size() != 10 || heightstr[4] !=
'-' || heightstr[7] !=
'-')
3507 year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4));
3509 month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2));
3510 day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2));
3512 catch (
const boost::bad_lexical_cast &)
3523 m_electrum_seed.
wipe();
3528 if (testnet && stagenet)
3530 fail_msg_writer() <<
tr(
"Can't specify more than one of --testnet and --stagenet");
3538 if (!handle_command_line(vm))
3541 bool welcome =
false;
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)
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\"");
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())
3550 if(!ask_wallet_create_if_needed())
return false;
3553 if (!m_generate_new.empty() || m_restoring)
3560 if (m_restore_deterministic_wallet || m_restore_multisig_wallet)
3562 if (m_non_deterministic)
3564 fail_msg_writer() <<
tr(
"can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic");
3567 if (!m_wallet_file.empty())
3569 if (m_restore_multisig_wallet)
3570 fail_msg_writer() <<
tr(
"--restore-multisig-wallet uses --generate-new-wallet, not --wallet-file");
3572 fail_msg_writer() <<
tr(
"--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
3576 if (m_electrum_seed.
empty())
3578 if (m_restore_multisig_wallet)
3580 const char *prompt =
"Specify multisig seed";
3581 m_electrum_seed = input_secure_line(prompt);
3584 if (m_electrum_seed.
empty())
3586 fail_msg_writer() <<
tr(
"specify a recovery parameter with the --electrum-seed=\"multisig seed here\"");
3592 m_electrum_seed =
"";
3595 const char *prompt = m_electrum_seed.
empty() ?
"Specify Electrum seed" :
"Electrum seed continued";
3599 if (electrum_seed.
empty())
3601 fail_msg_writer() <<
tr(
"specify a recovery parameter with the --electrum-seed=\"words list here\"");
3604 m_electrum_seed += electrum_seed;
3605 m_electrum_seed +=
' ';
3606 }
while (might_be_partial_seed(m_electrum_seed));
3610 if (m_restore_multisig_wallet)
3612 const boost::optional<epee::wipeable_string> parsed = m_electrum_seed.
parse_hexstr();
3618 multisig_keys = *parsed;
3629 auto pwd_container = password_prompter(
tr(
"Enter seed offset passphrase, empty if none"),
false);
3630 if (std::cin.eof() || !pwd_container)
3633 if (!seed_pass.
empty())
3635 if (m_restore_multisig_wallet)
3646 if (!m_generate_from_view_key.empty())
3648 m_wallet_file = m_generate_from_view_key;
3650 std::string address_string = input_line(
"Standard address");
3653 if (address_string.empty()) {
3663 if (
info.is_subaddress)
3665 fail_msg_writer() <<
tr(
"This address is a subaddress which cannot be used here.");
3673 if (viewkey_string.
empty()) {
3684 m_wallet_file=m_generate_from_view_key;
3692 if (
info.address.m_view_public_key != pkey) {
3697 auto r = new_wallet(vm,
info.address, boost::none, viewkey);
3702 else if (!m_generate_from_spend_key.empty())
3704 m_wallet_file = m_generate_from_spend_key;
3709 if (spendkey_string.
empty()) {
3718 auto r = new_wallet(vm, m_recovery_key,
true,
false,
"");
3723 else if (!m_generate_from_keys.empty())
3725 m_wallet_file = m_generate_from_keys;
3727 std::string address_string = input_line(
"Standard address");
3730 if (address_string.empty()) {
3740 if (
info.is_subaddress)
3742 fail_msg_writer() <<
tr(
"This address is a subaddress which cannot be used here.");
3750 if (spendkey_string.
empty()) {
3765 if (viewkey_string.
empty()) {
3776 m_wallet_file=m_generate_from_keys;
3784 if (
info.address.m_spend_public_key != pkey) {
3792 if (
info.address.m_view_public_key != pkey) {
3796 auto r = new_wallet(vm,
info.address,
spendkey, viewkey);
3803 else if (!m_generate_from_multisig_keys.empty())
3805 m_wallet_file = m_generate_from_multisig_keys;
3806 unsigned int multisig_m;
3807 unsigned int multisig_n;
3810 std::string multisig_type_string = input_line(
"Multisig type (input as M/N with M <= N and M > 1)");
3813 if (multisig_type_string.empty())
3818 if (sscanf(multisig_type_string.c_str(),
"%u/%u", &multisig_m, &multisig_n) != 2)
3820 fail_msg_writer() <<
tr(
"Error: expected M/N, but got: ") << multisig_type_string;
3823 if (multisig_m <= 1 || multisig_m > multisig_n)
3825 fail_msg_writer() <<
tr(
"Error: expected N > 1 and N <= M, but got: ") << multisig_type_string;
3828 if (multisig_m != multisig_n)
3833 message_writer() << boost::format(
tr(
"Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n;
3836 std::string address_string = input_line(
"Multisig wallet address");
3839 if (address_string.empty()) {
3854 if (viewkey_string.
empty())
3873 if (
info.address.m_view_public_key != pkey)
3882 if(multisig_m == multisig_n)
3884 std::vector<crypto::secret_key> multisig_secret_spendkeys(multisig_n);
3888 for(
unsigned int i=0; i<multisig_n; ++i)
3890 spendkey_string = input_secure_line(
tr((boost::format(
tr(
"Secret spend key (%u of %u)")) % (i+1) % multisig_m).str().c_str()));
3893 if (spendkey_string.
empty())
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]));
3923 if (
info.address.m_spend_public_key != pkey)
3930 auto r = new_wallet(vm,
info.address,
spendkey, viewkey);
3936 else if (!m_generate_from_json.empty())
3942 password = rc.second.password();
3943 m_wallet_file = m_wallet->path();
3945 catch (
const std::exception &e)
3953 else if (!m_generate_from_device.empty())
3955 m_wallet_file = m_generate_from_device;
3957 auto r = new_wallet(vm);
3962 if(m_wallet->get_refresh_from_block_height() == 0) {
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.");
3969 std::string confirm = input_line(
tr(
"Is this okay?"),
true);
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);
3981 if (m_generate_new.empty()) {
3982 fail_msg_writer() <<
tr(
"specify a wallet path with --generate-new-wallet (not --wallet-file)");
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);
3990 r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
3996 if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
4005 if (!datestr_to_int(m_restore_date, year, month, day))
4009 m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
4012 catch (
const std::runtime_error& e)
4019 if (!m_wallet->explicit_refresh_from_block_height() && m_restoring)
4022 bool connected = try_connect_to_daemon(
false, &
version);
4027 heightstr = input_line(
"Restore from specific blockchain height (optional, default 0)");
4029 heightstr = input_line(
"Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD)");
4032 if (heightstr.empty())
4034 m_restore_height = 0;
4039 m_restore_height = boost::lexical_cast<uint64_t>(heightstr);
4042 catch (
const boost::bad_lexical_cast &)
4054 if (!datestr_to_int(heightstr, year, month, day))
4056 m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
4058 std::string confirm = input_line(
tr(
"Is this okay?"),
true);
4064 catch (
const boost::bad_lexical_cast &)
4068 catch (
const std::runtime_error& e)
4077 uint64_t estimate_height = m_wallet->estimate_blockchain_height();
4078 if (m_restore_height >= estimate_height)
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);
4083 m_restore_height = 0;
4085 m_wallet->set_refresh_from_block_height(m_restore_height);
4087 m_wallet->rewrite(m_wallet_file, password);
4091 assert(!m_wallet_file.empty());
4092 if (!m_subaddress_lookahead.empty())
4094 fail_msg_writer() <<
tr(
"can't specify --subaddress-lookahead and --wallet-file at the same time");
4097 auto r = open_wallet(vm);
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();
4110 if (m_wallet->get_ring_database().empty())
4111 fail_msg_writer() <<
tr(
"Failed to initialize ring database: privacy enhancing features will be inactive");
4113 m_wallet->callback(
this);
4115 check_background_mining(password);
4118 message_writer(
console_color_yellow,
true) <<
tr(
"If you are new to Electroneum, type \"welcome\" for a brief overview.");
4135 if (!m_wallet.get())
4138 return close_wallet();
4141 bool simple_wallet::handle_command_line(
const boost::program_options::variables_map& vm)
4162 m_long_payment_id_support =
true;
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;
4177 if (!datestr_to_int(m_restore_date, year, month, day))
4184 bool simple_wallet::try_connect_to_daemon(
bool silent,
uint32_t*
version)
4189 if (!m_wallet->check_connection(
version))
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.");
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();
4213 std::string simple_wallet::get_mnemonic_language()
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;
4218 int language_number = -1;
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;
4224 std::vector<std::string>::const_iterator it;
4225 for (it = language_list.begin(), ii = 0; it != language_list.end(); it++, ii++)
4227 std::cout << ii <<
" : " << *it << std::endl;
4229 while (language_number < 0)
4231 language_choice = input_line(
tr(
"Enter the number corresponding to the language of your choice"));
4236 language_number = std::stoi(language_choice);
4237 if (!((language_number >= 0) && (
static_cast<unsigned int>(language_number) < language_list.size())))
4239 language_number = -1;
4240 fail_msg_writer() <<
tr(
"invalid language choice entered. Please try again.\n");
4243 catch (
const std::exception &e)
4245 fail_msg_writer() <<
tr(
"invalid language choice entered. Please try again.\n");
4248 return language_list_self[language_number];
4251 boost::optional<tools::password_container> simple_wallet::get_and_verify_password()
const
4253 auto pwd_container = default_password_prompter(m_wallet_file.empty());
4257 if (!m_wallet->verify_password(pwd_container->password()))
4262 return pwd_container;
4265 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(
const boost::program_options::variables_map& vm,
4276 if (!m_subaddress_lookahead.empty())
4280 m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
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())
4292 mnemonic_language = m_mnemonic_language;
4302 if (was_deprecated_wallet)
4306 "a deprecated version of the wallet. Please use the new seed that we provide.\n");
4308 mnemonic_language = get_mnemonic_language();
4309 if (mnemonic_language.empty())
4313 m_wallet->set_seed_language(mnemonic_language);
4320 recovery_val = m_wallet->generate(m_wallet_file,
std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
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);
4328 catch (
const std::exception& e)
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")
4352 print_seed(electrum_words);
4354 success_msg_writer() <<
"**********************************************************************";
4359 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(
const boost::program_options::variables_map& vm,
4371 if (!m_subaddress_lookahead.empty())
4375 m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
4378 if (m_restore_height)
4379 m_wallet->set_refresh_from_block_height(m_restore_height);
4381 if (m_account_major_offset)
4382 m_wallet->account_major_offset(m_account_major_offset);
4390 m_wallet->generate(m_wallet_file,
std::move(rc.second).password(),
address, *
spendkey, viewkey, create_address_file);
4394 m_wallet->generate(m_wallet_file,
std::move(rc.second).password(),
address, viewkey, create_address_file);
4397 << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4399 catch (
const std::exception& e)
4410 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(
const boost::program_options::variables_map& vm)
4414 m_wallet->callback(
this);
4421 if (!m_subaddress_lookahead.empty())
4425 m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
4428 if (m_restore_height)
4429 m_wallet->set_refresh_from_block_height(m_restore_height);
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);
4439 << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4441 catch (
const std::exception& e)
4450 boost::optional<epee::wipeable_string> simple_wallet::new_wallet(
const boost::program_options::variables_map& vm,
4461 if (!m_subaddress_lookahead.empty())
4465 m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second);
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())
4474 mnemonic_language = m_mnemonic_language;
4477 m_wallet->set_seed_language(mnemonic_language);
4483 m_wallet->generate(m_wallet_file,
std::move(rc.second).password(), multisig_keys, create_address_file);
4486 if (!m_wallet->multisig(&ready, &
threshold, &total) || !ready)
4492 << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4494 catch (
const std::exception& e)
4503 boost::optional<epee::wipeable_string> simple_wallet::open_wallet(
const boost::program_options::variables_map& vm)
4511 bool keys_file_exists;
4512 bool wallet_file_exists;
4515 if(!keys_file_exists)
4532 m_wallet->callback(
this);
4533 m_wallet->load(m_wallet_file, password);
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();
4542 prefix =
tr(
"Opened wallet");
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();
4551 if (m_wallet->is_deprecated())
4553 bool is_deterministic;
4556 is_deterministic = m_wallet->is_deterministic();
4558 if (is_deterministic)
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())
4565 m_wallet->set_seed_language(mnemonic_language);
4566 m_wallet->rewrite(m_wallet_file, password);
4570 m_wallet->get_seed(seed);
4576 "a deprecated version of the wallet. Your wallet file format is being upgraded now.\n");
4577 m_wallet->rewrite(m_wallet_file, password);
4581 catch (
const std::exception& e)
4587 bool password_is_correct =
false;
4590 password_is_correct = m_wallet->verify_password(password);
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;
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 "**********************************************************************";
4606 bool simple_wallet::close_wallet()
4608 if (m_idle_run.load(std::memory_order_relaxed))
4610 m_idle_run.store(
false, std::memory_order_relaxed);
4613 boost::unique_lock<boost::mutex> lock(m_idle_mutex);
4614 m_idle_cond.notify_one();
4616 m_idle_thread.join();
4619 bool r = m_wallet->deinit();
4630 catch (
const std::exception& e)
4647 catch (
const std::exception& e)
4655 bool simple_wallet::save_watch_only(
const std::vector<std::string> &args)
4657 if (m_wallet->multisig())
4659 fail_msg_writer() <<
tr(
"wallet is multisig and cannot save a watch-only version");
4663 const auto pwd_container = password_prompter(
tr(
"Password for new watch-only wallet"),
true);
4674 m_wallet->write_watch_only_wallet(m_wallet_file, pwd_container->password(), new_keys_filename);
4677 catch (
const std::exception &e)
4685 void simple_wallet::start_background_mining()
4689 bool r = m_wallet->invoke_http_json(
"/mining_status", reqq, resq);
4690 std::string err = interpret_rpc_response(r, resq.status);
4698 if (!resq.is_background_mining_enabled)
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);
4714 success_msg_writer() <<
tr(
"Background mining enabled. Thank you for supporting the Electroneum network.");
4717 void simple_wallet::stop_background_mining()
4721 bool r = m_wallet->invoke_http_json(
"/mining_status", reqq, resq);
4724 std::string err = interpret_rpc_response(r, resq.status);
4730 if (resq.is_background_mining_enabled)
4734 bool r = m_wallet->invoke_http_json(
"/stop_mining", req,
res);
4754 if (!m_wallet->is_trusted_daemon())
4762 bool r = m_wallet->invoke_http_json(
"/mining_status", req,
res);
4764 bool is_background_mining_enabled =
false;
4766 is_background_mining_enabled =
res.is_background_mining_enabled;
4768 if (is_background_mining_enabled)
4772 m_wallet->rewrite(m_wallet_file, password);
4773 start_background_mining();
4802 bool simple_wallet::start_mining(
const std::vector<std::string>& args)
4804 if (!m_wallet->is_trusted_daemon())
4806 fail_msg_writer() <<
tr(
"this command requires a trusted daemon. Enable with --trusted-daemon");
4810 if (!try_connect_to_daemon())
4819 req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
4822 size_t arg_size = args.size();
4825 if (!parse_bool_and_use(args[2], [&](
bool r) { req.ignore_battery = r; }))
4830 if (!parse_bool_and_use(args[1], [&](
bool r) { req.do_background_mining = r; }))
4837 ok = ok && 1 <= num;
4838 req.threads_count = num;
4842 req.threads_count = 1;
4852 bool r = m_wallet->invoke_http_json(
"/start_mining", req,
res);
4861 bool simple_wallet::stop_mining(
const std::vector<std::string>& args)
4863 if (!try_connect_to_daemon())
4874 bool r = m_wallet->invoke_http_json(
"/stop_mining", req,
res);
4883 bool simple_wallet::set_daemon(
const std::vector<std::string>& args)
4887 if (args.size() < 1)
4893 boost::regex rgx(
"^(.*://)?([A-Za-z0-9\\-\\.]+)(:[0-9]+)?");
4894 boost::cmatch match;
4896 if (boost::regex_match(args[0].c_str(), match, rgx))
4898 if (match.length() < 4)
4900 fail_msg_writer() <<
tr(
"Unexpected array length - Exited simple_wallet::set_daemon()");
4904 if (!match[3].length())
4909 daemon_url = args[0];
4912 m_wallet->init(daemon_url);
4914 if (args.size() == 2)
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);
4922 fail_msg_writer() <<
tr(
"Expected trusted or untrusted, got ") << args[1] <<
": assuming untrusted";
4923 m_wallet->set_trusted_daemon(
false);
4928 m_wallet->set_trusted_daemon(
false);
4933 MINFO(
tr(
"Daemon is local, assuming trusted"));
4934 m_wallet->set_trusted_daemon(
true);
4937 catch (
const std::exception &e) { }
4939 success_msg_writer() << boost::format(
"Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ?
tr(
"trusted") :
tr(
"untrusted"));
4946 bool simple_wallet::save_bc(
const std::vector<std::string>& args)
4948 if (!try_connect_to_daemon())
4958 bool r = m_wallet->invoke_http_json(
"/save_bc", req,
res);
4969 if (!m_auto_refresh_refreshing)
4970 m_refresh_progress_reporter.update(
height,
false);
4977 tr(
"txid ") << txid <<
", " <<
4979 tr(
"idx ") << subaddr_index;
4981 const uint64_t warn_height = m_wallet->nettype() ==
TESTNET ? 1000000 : m_wallet->nettype() ==
STAGENET ? 50000 : 1650000;
4982 if (
height >= warn_height)
4984 std::vector<tx_extra_field> tx_extra_fields;
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."));
4997 if (m_auto_refresh_refreshing)
5000 m_refresh_progress_reporter.update(
height,
true);
5012 tr(
"txid ") << txid <<
", " <<
5014 tr(
"idx ") << subaddr_index;
5015 if (m_auto_refresh_refreshing)
5018 m_refresh_progress_reporter.update(
height,
true);
5025 boost::optional<epee::wipeable_string> simple_wallet::on_get_password(
const char *reason)
5028 if (!m_in_manual_refresh.load(std::memory_order_relaxed))
5030 message_writer(
console_color_red,
false) << boost::format(
tr(
"Password needed (%s) - use the refresh command")) % reason;
5035 #ifdef HAVE_READLINE
5039 if (reason && *reason)
5044 MERROR(
"Failed to read password");
5048 return pwd_container->password();
5051 void simple_wallet::on_device_button_request(
uint64_t code)
5056 boost::optional<epee::wipeable_string> simple_wallet::on_device_pin_request()
5058 #ifdef HAVE_READLINE
5064 return pwd_container->password();
5067 boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_request(
bool on_device)
5070 message_writer(
console_color_white,
true) <<
tr(
"Please enter the device passphrase on the device");
5074 #ifdef HAVE_READLINE
5080 return pwd_container->password();
5083 void simple_wallet::on_refresh_finished(
uint64_t start_height,
uint64_t fetched_blocks,
bool is_init,
bool received_etn)
5086 if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) {
5090 if (!received_etn || m_wallet->get_device_last_key_image_sync() != 0) {
5095 message_writer() <<
"\n" <<
tr(
"The first refresh has finished for the HW-based wallet with received ETN. hw_key_images_sync is needed. ");
5097 std::string accepted = input_line(
tr(
"Do you want to do it now? (Y/Yes/N/No): "));
5099 message_writer(
console_color_red,
false) <<
tr(
"hw_key_images_sync skipped. Run command manually before a transfer.");
5103 key_images_sync_intern();
5106 bool simple_wallet::refresh_main(
uint64_t start_height,
enum ResetType reset,
bool is_init)
5108 if (!try_connect_to_daemon(is_init))
5114 uint64_t height_pre = 0, height_post;
5115 if (reset != ResetNone)
5117 if (reset == ResetSoftKeepKI)
5118 height_pre = m_wallet->hash_m_transfers(-1, transfer_hash_pre);
5120 m_wallet->rescan_blockchain(reset == ResetHard,
false, reset == ResetSoftKeepKI);
5123 #ifdef HAVE_READLINE
5127 message_writer() <<
tr(
"Starting refresh...");
5130 bool received_etn =
false;
5132 std::ostringstream ss;
5135 m_in_manual_refresh.store(
true, std::memory_order_relaxed);
5137 m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_etn);
5139 if (reset == ResetSoftKeepKI)
5141 m_wallet->finish_rescan_bc_keep_key_images(height_pre, transfer_hash_pre);
5143 height_post = m_wallet->get_num_transfer_details();
5144 if (height_pre != height_post)
5146 message_writer() <<
tr(
"New transfer received since rescan was started. Key images are incomplete.");
5152 std::cout <<
"\r \r";
5156 show_balance_unlocked();
5157 on_refresh_finished(start_height, fetched_blocks, is_init, received_etn);
5161 ss <<
tr(
"daemon is busy. Please try again later.");
5165 ss <<
tr(
"no connection to daemon. Please make sure daemon is running.");
5170 ss <<
tr(
"RPC error: ") << e.what();
5175 ss <<
tr(
"refresh error: ") << e.what();
5180 ss <<
tr(
"internal error: ") << e.what();
5182 catch (
const std::exception& e)
5184 LOG_ERROR(
"unexpected error: " << e.what());
5185 ss <<
tr(
"unexpected error: ") << e.what();
5190 ss <<
tr(
"unknown error");
5195 fail_msg_writer() <<
tr(
"refresh failed: ") << ss.str() <<
". " <<
tr(
"Blocks received: ") << fetched_blocks;
5201 bool simple_wallet::refresh(
const std::vector<std::string>& args)
5207 start_height = boost::lexical_cast<uint64_t>( args[0] );
5209 catch(
const boost::bad_lexical_cast &)
5214 return refresh_main(start_height, ResetNone);
5217 bool simple_wallet::show_balance_unlocked(
bool detailed)
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)");
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];
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;
5234 if (blocks_to_unlock > 0) {
5235 unlock_time_message = (boost::format(
" (%lu block(s) to unlock)") % blocks_to_unlock).str();
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();
5241 if (!m_wallet->synced_to_v10() || m_wallet->balance(m_current_subaddress_account,
false)) {
5244 <<
tr(
"Pre V10 unlocked balance: ") <<
print_etn(unlocked_balance) << unlock_time_message << extra;
5246 if (m_wallet->balance(m_current_subaddress_account,
true)) {
5249 <<
tr(
"Unlocked balance: ") <<
print_etn(unlocked_balance_public_chain) << unlock_time_message_public_chain << extra;
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);
5257 if (!detailed || (balance_per_subaddress.empty() && balance_per_subaddress_public_chain.empty()))
5260 if (!balance_per_subaddress.empty()) {
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) {
5269 std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6);
5270 uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](
5272 return td.m_tx.version == 1 && !td.m_spent && td.m_subaddr_index == subaddr_index;
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);
5280 if (!balance_per_subaddress_public_chain.empty()) {
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) {
5289 std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6);
5290 uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](
5292 return td.m_tx.version > 1 && !td.m_spent && td.m_subaddr_index == subaddr_index;
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);
5304 bool simple_wallet::show_balance(
const std::vector<std::string>& args)
5306 if (args.size() > 1 || (args.size() == 1 && args[0] !=
"detail"))
5312 show_balance_unlocked(args.size() == 1);
5316 bool simple_wallet::show_incoming_transfers(
const std::vector<std::string>& args)
5318 if (args.size() > 3)
5323 auto local_args = args;
5326 bool filter =
false;
5327 bool available =
false;
5330 if (local_args.size() > 0)
5332 if (local_args[0] ==
"available")
5336 local_args.erase(local_args.begin());
5338 else if (local_args[0] ==
"unavailable")
5342 local_args.erase(local_args.begin());
5345 while (local_args.size() > 0)
5347 if (local_args[0] ==
"verbose")
5349 else if (local_args[0] ==
"uses")
5356 local_args.erase(local_args.begin());
5359 const uint64_t blockchain_height = m_wallet->get_blockchain_current_height();
5363 std::set<uint32_t> subaddr_indices;
5364 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
5366 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
5368 local_args.erase(local_args.begin());
5371 if (local_args.size() > 0)
5378 m_wallet->get_transfers(transfers);
5380 size_t transfers_found = 0;
5381 for (
const auto& td : transfers)
5383 if (!filter || available != td.m_spent)
5385 if (m_current_subaddress_account != td.m_subaddr_index.major || (!subaddr_indices.empty() && subaddr_indices.count(td.m_subaddr_index.minor) == 0))
5387 if (!transfers_found)
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;
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();
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;
5405 boost::format(
"%21s%8s%12s%8s%16u%68s%16u%s") %
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 %
5412 td.m_subaddr_index.minor %
5418 if (!transfers_found)
5435 success_msg_writer() << boost::format(
"Found %u/%u transfers") % transfers_found % transfers.size();
5441 bool simple_wallet::show_payments(
const std::vector<std::string> &args)
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");
5456 bool payments_found =
false;
5462 std::list<tools::wallet2::payment_details> payments;
5463 m_wallet->get_payments(payment_id, payments);
5464 if(payments.empty())
5474 payments_found =
true;
5477 boost::format(
"%68s%68s%12s%21s%16s%16s") %
5483 pd.m_subaddr_index.minor;
5488 fail_msg_writer() <<
tr(
"payment ID has invalid format, expected 16 or 64 character hex string: ") << arg;
5499 throw std::runtime_error(
"simple_wallet null wallet");
5501 return m_wallet->get_daemon_blockchain_height(err);
5504 bool simple_wallet::show_blockchain_height(
const std::vector<std::string>& args)
5506 if (!try_connect_to_daemon())
5510 uint64_t bc_height = get_daemon_blockchain_height(err);
5518 bool simple_wallet::rescan_spent(
const std::vector<std::string> &args)
5520 if (!m_wallet->is_trusted_daemon())
5522 fail_msg_writer() <<
tr(
"this command requires a trusted daemon. Enable with --trusted-daemon");
5526 if (!try_connect_to_daemon())
5532 m_wallet->rescan_spent();
5540 fail_msg_writer() <<
tr(
"no connection to daemon. Please make sure daemon is running.");
5551 catch (
const std::exception& e)
5553 LOG_ERROR(
"unexpected error: " << e.what());
5565 std::pair<std::string, std::string> simple_wallet::show_outputs_line(
const std::vector<uint64_t> &heights,
uint64_t blockchain_height,
uint64_t highlight_height)
const
5567 std::stringstream ostr;
5570 blockchain_height = std::max(blockchain_height, h);
5572 for (
size_t j = 0; j < heights.size(); ++j)
5573 ostr << (heights[j] == highlight_height ?
" *" :
" ") << heights[j];
5578 for (
size_t j = 0; j < heights.size(); ++j)
5580 uint64_t pos = (heights[j] * resolution) / blockchain_height;
5581 ring_str[pos] =
'o';
5583 if (highlight_height < blockchain_height)
5585 uint64_t pos = (highlight_height * resolution) / blockchain_height;
5586 ring_str[pos] =
'*';
5589 return std::make_pair(ostr.str(), ring_str);
5592 bool simple_wallet::print_ring_members(
const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
5595 if (!try_connect_to_daemon(
false, &
version))
5601 uint64_t blockchain_height = get_daemon_blockchain_height(err);
5608 for (
size_t n = 0; n < ptx_vector.size(); ++n)
5614 std::vector<uint64_t> spent_key_height(tx.
vin.size());
5615 std::vector<crypto::hash> spent_key_txid (tx.
vin.size());
5616 for (
size_t i = 0; i < tx.
vin.size(); ++i)
5623 for (
const auto &src: construction_data.
sources)
5624 if (src.outputs[src.real_output].second.dest == td.
get_public_key())
5638 req.outputs.resize(absolute_offsets.size());
5639 for (
size_t j = 0; j < absolute_offsets.size(); ++j)
5641 req.outputs[j].amount = in_key.
amount;
5642 req.outputs[j].index = absolute_offsets[j];
5645 bool r = m_wallet->invoke_http_bin(
"/get_outs.bin", req,
res);
5646 err = interpret_rpc_response(r,
res.status);
5653 for (
auto& res_out :
res.outs)
5655 if (res_out.height >= blockchain_height)
5657 fail_msg_writer() <<
tr(
"output key's originating block height shouldn't be higher than the blockchain height");
5661 ostr <<
tr(
"\nOriginating block heights: ");
5662 spent_key_height[i] =
res.outs[
source.real_output].height;
5663 spent_key_txid [i] =
res.outs[
source.real_output].txid;
5664 std::vector<uint64_t> heights(absolute_offsets.size(), 0);
5665 uint64_t highlight_height = std::numeric_limits<uint64_t>::max();
5666 for (
size_t j = 0; j < absolute_offsets.size(); ++j)
5668 heights[j] =
res.outs[j].height;
5669 if (j ==
source.real_output)
5670 highlight_height = heights[j];
5672 std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
5673 ostr << ring_str.first <<
tr(
"\n|") << ring_str.second <<
tr(
"|\n");
5676 bool are_keys_from_same_tx =
false;
5677 bool are_keys_from_close_height =
false;
5678 for (
size_t i = 0; i < tx.
vin.size(); ++i) {
5679 for (
size_t j = i + 1; j < tx.
vin.size(); ++j)
5681 if (spent_key_txid[i] == spent_key_txid[j])
5682 are_keys_from_same_tx =
true;
5683 if (std::abs((
int64_t)(spent_key_height[i] - spent_key_height[j])) < (
int64_t)5)
5684 are_keys_from_close_height =
true;
5687 if (are_keys_from_same_tx || are_keys_from_close_height)
5690 <<
tr(
"\nWarning: Some input keys being spent are from ")
5691 << (are_keys_from_same_tx ?
tr(
"the same transaction") :
tr(
"blocks that are temporally very close"))
5692 <<
tr(
", which can break the anonymity of ring signature. Make sure this is intentional!");
5699 bool simple_wallet::transfer_main(
int transfer_type,
const std::vector<std::string> &args_,
bool called_by_mms)
5702 if (!try_connect_to_daemon())
5705 std::vector<std::string> local_args = args_;
5707 std::set<uint32_t> subaddr_indices;
5708 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
5710 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
5712 local_args.erase(local_args.begin());
5716 if (local_args.size() > 0 &&
parse_priority(local_args[0], priority))
5717 local_args.erase(local_args.begin());
5719 priority = m_wallet->adjust_priority(priority);
5721 size_t fake_outs_count = 0;
5722 if(local_args.size() > 0) {
5726 fake_outs_count = m_wallet->default_mixin();
5727 if (fake_outs_count == 0)
5730 else if (ring_size == 0)
5737 fake_outs_count = ring_size - 1;
5738 local_args.erase(local_args.begin());
5742 uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
5743 if (adjusted_fake_outs_count > fake_outs_count)
5745 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
5748 if (adjusted_fake_outs_count < fake_outs_count)
5750 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
5754 const size_t min_args = (transfer_type ==
TransferLocked) ? 2 : 1;
5755 if(local_args.size() < min_args)
5761 std::vector<uint8_t> extra;
5762 bool payment_id_seen =
false;
5763 if (!local_args.empty())
5775 local_args.pop_back();
5776 payment_id_seen =
true;
5777 message_writer() <<
tr(
"Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead");
5791 locked_blocks = boost::lexical_cast<uint64_t>(local_args.back());
5793 catch (
const std::exception &e)
5795 fail_msg_writer() <<
tr(
"bad locked_blocks parameter:") <<
" " << local_args.back();
5798 if (locked_blocks > 1000000)
5803 local_args.pop_back();
5806 vector<cryptonote::address_parse_info> dsts_info;
5807 vector<cryptonote::tx_destination_entry> dsts;
5808 size_t num_subaddresses = 0;
5809 for (
size_t i = 0; i < local_args.size(); )
5811 dsts_info.emplace_back();
5817 std::string address_uri, payment_id_uri, tx_description, recipient_name,
error;
5818 std::vector<std::string> unknown_parameters;
5820 bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters,
error);
5824 if (payment_id_uri.size() == 16)
5831 info.has_payment_id =
true;
5837 else if (i + 1 < local_args.size())
5841 if(!ok || 0 == de.
amount)
5843 fail_msg_writer() <<
tr(
"amount is wrong: ") << local_args[i] <<
' ' << local_args[i + 1] <<
5844 ", " <<
tr(
"expected number from 0 to ") <<
print_etn(std::numeric_limits<uint64_t>::max());
5852 if (boost::starts_with(local_args[i],
"electroneum:"))
5867 num_subaddresses +=
info.is_subaddress;
5869 if (
info.has_payment_id || !payment_id_uri.empty())
5871 if (payment_id_seen)
5873 fail_msg_writer() <<
tr(
"a single transaction cannot use more than one payment id");
5879 if (
info.has_payment_id)
5881 memcpy(payment_id.data,
info.payment_id.data, 8);
5882 memset(payment_id.data + 8, 0, 24);
5898 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
5901 payment_id_seen =
true;
5908 if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
5910 std::string accepted = input_line(
tr(
"No payment id is included with this transaction. Is this okay?"),
true);
5926 std::vector<tools::wallet2::pending_tx> ptx_vector;
5927 uint64_t bc_height, unlock_block = 0;
5929 switch (transfer_type)
5932 bc_height = get_daemon_blockchain_height(err);
5938 unlock_block = bc_height + locked_blocks;
5939 ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block , priority, extra, m_current_subaddress_account, subaddr_indices);
5942 LOG_ERROR(
"Unknown transfer method, using default");
5945 ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 , priority, extra, m_current_subaddress_account, subaddr_indices);
5949 if (ptx_vector.empty())
5956 if (m_wallet->confirm_backlog())
5958 std::stringstream prompt;
5959 double worst_fee_per_byte = std::numeric_limits<double>::max();
5960 for (
size_t n = 0; n < ptx_vector.size(); ++n)
5963 const double fee_per_byte = ptx_vector[n].fee / (double)blob_size;
5964 if (fee_per_byte < worst_fee_per_byte)
5966 worst_fee_per_byte = fee_per_byte;
5971 std::vector<std::pair<uint64_t, uint64_t>> nblocks = m_wallet->estimate_backlog({std::make_pair(worst_fee_per_byte, worst_fee_per_byte)});
5972 if (nblocks.size() != 1)
5974 prompt <<
"Internal error checking for backlog. " <<
tr(
"Is this okay anyway?");
5978 if (nblocks[0].first > m_wallet->get_confirm_backlog_threshold())
5979 prompt << (boost::format(
tr(
"There is currently a %u block backlog at that fee level. Is this okay?")) % nblocks[0].first).str();
5982 catch (
const std::exception &e)
5984 prompt <<
tr(
"Failed to check for backlog: ") << e.what() <<
ENDL <<
tr(
"Is this okay anyway?");
5988 if (!prompt_str.empty())
5990 std::string accepted = input_line(prompt_str,
true);
6003 if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
6010 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6012 total_fee += ptx_vector[n].fee;
6013 for (
auto i: ptx_vector[n].selected_transfers)
6014 total_sent += m_wallet->get_transfer_details(i).amount();
6015 total_sent -= ptx_vector[n].change_dts.amount + ptx_vector[n].fee;
6016 change += ptx_vector[n].change_dts.amount;
6018 if (ptx_vector[n].dust_added_to_fee)
6019 dust_in_fee += ptx_vector[n].dust;
6021 dust_not_in_fee += ptx_vector[n].dust;
6024 std::stringstream prompt;
6025 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6027 prompt <<
tr(
"\nTransaction ") << (n + 1) <<
"/" << ptx_vector.size() <<
":\n";
6028 subaddr_indices.clear();
6030 subaddr_indices.insert(i);
6032 prompt << boost::format(
tr(
"Spending from address index %d\n")) % i;
6033 if (subaddr_indices.size() > 1)
6034 prompt <<
tr(
"WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
6036 prompt << boost::format(
tr(
"Sending %s. ")) %
print_etn(total_sent);
6037 if (ptx_vector.size() > 1)
6039 prompt << boost::format(
tr(
"Your transaction needs to be split into %llu transactions. "
6040 "This will result in a transaction fee being applied to each transaction, for a total fee of %s")) %
6041 ((
unsigned long long)ptx_vector.size()) %
print_etn(total_fee);
6045 prompt << boost::format(
tr(
"The transaction fee is %s")) %
6048 if (dust_in_fee != 0) prompt << boost::format(
tr(
", of which %s is dust from change")) %
print_etn(dust_in_fee);
6049 if (dust_not_in_fee != 0) prompt <<
tr(
".") <<
ENDL << boost::format(
tr(
"A total of %s from dust change will be sent to dust address"))
6053 float days = locked_blocks / 720.0f;
6054 prompt << boost::format(
tr(
".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) %
cryptonote::print_etn(change) % ((
unsigned long long)unlock_block) % days;
6056 if (m_wallet->print_ring_members())
6058 if (!print_ring_members(ptx_vector, prompt))
6061 bool default_ring_size =
true;
6062 for (
const auto &ptx: ptx_vector)
6064 for (
const auto &vin: ptx.tx.vin)
6068 const txin_to_key& in_to_key = boost::get<txin_to_key>(vin);
6070 default_ring_size =
false;
6074 if (m_wallet->confirm_non_default_ring_size() && !default_ring_size)
6076 prompt <<
tr(
"WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
6078 prompt <<
ENDL <<
tr(
"Is this okay?");
6080 std::string accepted = input_line(prompt.str(),
true);
6093 if (m_wallet->multisig() && called_by_mms)
6095 std::string ciphertext = m_wallet->save_multisig_tx(ptx_vector);
6096 if (!ciphertext.empty())
6102 else if (m_wallet->multisig())
6104 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6112 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6115 else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6125 commit_or_save(signed_tx.
ptx, m_do_not_relay);
6127 catch (
const std::exception& e)
6129 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6139 else if (m_wallet->watch_only())
6141 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6149 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6154 commit_or_save(ptx_vector, m_do_not_relay);
6157 catch (
const std::exception &e)
6159 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6172 bool simple_wallet::transfer(
const std::vector<std::string> &args_)
6174 transfer_main(
Transfer, args_,
false);
6178 bool simple_wallet::locked_transfer(
const std::vector<std::string> &args_)
6184 bool simple_wallet::locked_sweep_all(
const std::vector<std::string> &args_)
6186 return sweep_main(0,
true, args_);
6190 bool simple_wallet::sweep_unmixable(
const std::vector<std::string> &args_)
6192 if (!try_connect_to_daemon())
6200 auto ptx_vector = m_wallet->create_unmixable_sweep_transactions();
6202 if (ptx_vector.empty())
6209 uint64_t total_fee = 0, total_unmixable = 0;
6210 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6212 total_fee += ptx_vector[n].fee;
6213 for (
auto i: ptx_vector[n].selected_transfers)
6214 total_unmixable += m_wallet->get_transfer_details(i).amount();
6218 if (ptx_vector.size() > 1) {
6219 prompt_str = (boost::format(
tr(
"Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6221 ((
unsigned long long)ptx_vector.size()) %
6225 prompt_str = (boost::format(
tr(
"Sweeping %s for a total fee of %s. Is this okay?")) %
6229 std::string accepted = input_line(prompt_str,
true);
6240 if (m_wallet->multisig())
6242 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6249 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6252 else if (m_wallet->watch_only())
6254 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6261 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6266 commit_or_save(ptx_vector, m_do_not_relay);
6272 std::string accepted = input_line((boost::format(
tr(
"Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay?")) %
print_etn(e.
available())).str(),
true);
6279 m_wallet->discard_unmixable_outputs();
6283 catch (
const std::exception &e)
6285 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6296 bool simple_wallet::sweep_main(
uint64_t below,
bool locked,
const std::vector<std::string> &args_)
6298 auto print_usage = [below]()
6309 if (args_.size() == 0)
6316 if (!try_connect_to_daemon())
6319 std::vector<std::string> local_args = args_;
6321 std::set<uint32_t> subaddr_indices;
6322 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
6324 if (local_args[0] ==
"index=all")
6326 for (
uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i)
6327 subaddr_indices.insert(i);
6329 else if (!parse_subaddress_indices(local_args[0], subaddr_indices))
6334 local_args.erase(local_args.begin());
6338 if (local_args.size() > 0 &&
parse_priority(local_args[0], priority))
6339 local_args.erase(local_args.begin());
6341 priority = m_wallet->adjust_priority(priority);
6343 size_t fake_outs_count = 0;
6344 if(local_args.size() > 0) {
6348 fake_outs_count = m_wallet->default_mixin();
6349 if (fake_outs_count == 0)
6352 else if (ring_size == 0)
6359 fake_outs_count = ring_size - 1;
6360 local_args.erase(local_args.begin());
6364 uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6365 if (adjusted_fake_outs_count > fake_outs_count)
6367 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6370 if (adjusted_fake_outs_count < fake_outs_count)
6372 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6380 if (local_args.size() < 2) {
6387 locked_blocks = boost::lexical_cast<uint64_t>(local_args[1]);
6389 catch (
const std::exception &e)
6394 if (locked_blocks > 1000000)
6400 uint64_t bc_height = get_daemon_blockchain_height(err);
6406 unlock_block = bc_height + locked_blocks;
6408 local_args.erase(local_args.begin() + 1);
6412 if (local_args.size() > 0 && local_args[0].substr(0, 8) ==
"outputs=")
6419 else if (outputs < 1)
6426 local_args.erase(local_args.begin());
6430 std::vector<uint8_t> extra;
6431 bool payment_id_seen =
false;
6432 if (local_args.size() >= 2)
6445 payment_id_seen =
true;
6448 if(!r && local_args.size() == 3)
6450 fail_msg_writer() <<
tr(
"payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str;
6454 if (payment_id_seen)
6455 local_args.pop_back();
6466 if (
info.has_payment_id)
6468 if (payment_id_seen)
6470 fail_msg_writer() <<
tr(
"a single transaction cannot use more than one payment id: ") << local_args[0];
6476 memcpy(payment_id.data,
info.payment_id.data, 8);
6477 memset(payment_id.data + 8, 0, 24);
6483 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
6486 payment_id_seen =
true;
6490 if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !
info.is_subaddress)
6492 std::string accepted = input_line(
tr(
"No payment id is included with this transaction. Is this okay?"),
true);
6508 auto ptx_vector = m_wallet->create_transactions_all(below,
info.address,
info.is_subaddress, outputs, fake_outs_count, unlock_block , priority, extra, m_current_subaddress_account, subaddr_indices);
6510 if (ptx_vector.empty())
6517 uint64_t total_fee = 0, total_sent = 0;
6518 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6520 total_fee += ptx_vector[n].fee;
6521 for (
auto i: ptx_vector[n].selected_transfers)
6522 total_sent += m_wallet->get_transfer_details(i).amount();
6525 std::ostringstream prompt;
6526 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6528 prompt <<
tr(
"\nTransaction ") << (n + 1) <<
"/" << ptx_vector.size() <<
":\n";
6529 subaddr_indices.clear();
6531 subaddr_indices.insert(i);
6533 prompt << boost::format(
tr(
"Spending from address index %d\n")) % i;
6534 if (subaddr_indices.size() > 1)
6535 prompt <<
tr(
"WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
6537 if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
6539 if (ptx_vector.size() > 1) {
6540 prompt << boost::format(
tr(
"Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6542 ((
unsigned long long)ptx_vector.size()) %
6546 prompt << boost::format(
tr(
"Sweeping %s for a total fee of %s. Is this okay?")) %
6550 std::string accepted = input_line(prompt.str(),
true);
6561 if (m_wallet->multisig())
6563 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6570 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6573 else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6578 std::vector<cryptonote::address_parse_info> dsts_info;
6579 dsts_info.push_back(
info);
6586 commit_or_save(signed_tx.
ptx, m_do_not_relay);
6588 catch (
const std::exception& e)
6590 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6598 else if (m_wallet->watch_only())
6600 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6607 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6612 commit_or_save(ptx_vector, m_do_not_relay);
6615 catch (
const std::exception& e)
6617 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6628 bool simple_wallet::sweep_single(
const std::vector<std::string> &args_)
6630 if (!try_connect_to_daemon())
6633 std::vector<std::string> local_args = args_;
6636 if (local_args.size() > 0 &&
parse_priority(local_args[0], priority))
6637 local_args.erase(local_args.begin());
6639 priority = m_wallet->adjust_priority(priority);
6641 size_t fake_outs_count = 0;
6642 if(local_args.size() > 0) {
6646 fake_outs_count = m_wallet->default_mixin();
6647 if (fake_outs_count == 0)
6650 else if (ring_size == 0)
6657 fake_outs_count = ring_size - 1;
6658 local_args.erase(local_args.begin());
6662 uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6663 if (adjusted_fake_outs_count > fake_outs_count)
6665 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6668 if (adjusted_fake_outs_count < fake_outs_count)
6670 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6675 if (local_args.size() > 0 && local_args[0].substr(0, 8) ==
"outputs=")
6682 else if (outputs < 1)
6689 local_args.erase(local_args.begin());
6693 std::vector<uint8_t> extra;
6694 bool payment_id_seen =
false;
6695 if (local_args.size() == 3)
6713 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
6717 local_args.pop_back();
6718 payment_id_seen =
true;
6721 if (local_args.size() != 2)
6741 if (
info.has_payment_id)
6743 if (payment_id_seen)
6745 fail_msg_writer() <<
tr(
"a single transaction cannot use more than one payment id: ") << local_args[0];
6751 memcpy(payment_id.data,
info.payment_id.data, 8);
6752 memset(payment_id.data + 8, 0, 24);
6757 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
6760 payment_id_seen =
true;
6764 if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !
info.is_subaddress)
6766 std::string accepted = input_line(
tr(
"No payment id is included with this transaction. Is this okay?"),
true);
6784 auto ptx_vector = m_wallet->create_transactions_single(ki,
info.address,
info.is_subaddress, outputs, fake_outs_count, 0 , priority, extra);
6786 if (ptx_vector.empty())
6791 if (ptx_vector.size() > 1)
6793 fail_msg_writer() <<
tr(
"Multiple transactions are created, which is not supposed to happen");
6796 if (ptx_vector[0].selected_transfers.size() != 1)
6798 fail_msg_writer() <<
tr(
"The transaction uses multiple or no inputs, which is not supposed to happen");
6803 uint64_t total_fee = ptx_vector[0].fee;
6804 uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount();
6805 std::ostringstream prompt;
6806 if (!print_ring_members(ptx_vector, prompt))
6808 prompt << boost::format(
tr(
"Sweeping %s for a total fee of %s. Is this okay?")) %
6811 std::string accepted = input_line(prompt.str(),
true);
6821 if (m_wallet->multisig())
6823 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6830 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6833 else if (m_wallet->watch_only())
6835 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6842 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6847 m_wallet->commit_tx(ptx_vector[0]);
6852 catch (
const std::exception& e)
6854 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6865 bool simple_wallet::sweep_all(
const std::vector<std::string> &args_)
6867 return sweep_main(0,
false, args_);
6870 bool simple_wallet::sweep_below(
const std::vector<std::string> &args_)
6873 if (args_.size() < 1)
6883 return sweep_main(below,
false, std::vector<std::string>(++args_.begin(), args_.end()));
6886 bool simple_wallet::donate(
const std::vector<std::string> &args_)
6888 std::vector<std::string> local_args = args_;
6889 if(local_args.empty() || local_args.size() > 5)
6902 payment_id_str = local_args.back();
6903 local_args.pop_back();
6908 if (ok && amount != 0)
6910 amount_str = local_args.back();
6911 local_args.pop_back();
6915 fail_msg_writer() <<
tr(
"amount is wrong: ") << local_args.back() <<
", " <<
tr(
"expected number from 0 to ") <<
print_etn(std::numeric_limits<uint64_t>::max());
6935 local_args.push_back(address_str);
6936 local_args.push_back(amount_str);
6937 if (!payment_id_str.empty())
6938 local_args.push_back(payment_id_str);
6943 transfer(local_args);
6950 uint64_t amount = 0, amount_to_dests = 0, change = 0;
6951 size_t min_ring_size = ~0;
6952 std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
6953 int first_known_non_zero_change_index = -1;
6955 for (
size_t n = 0; n < get_num_txes(); ++n)
6959 std::vector<tx_extra_field> tx_extra_fields;
6960 bool has_encrypted_payment_id =
false;
6970 if (!payment_id_string.empty())
6971 payment_id_string +=
", ";
6974 bool is_dummy =
true;
6975 for (
const auto &e: cd.
dests)
6976 if (e.is_integrated)
6981 payment_id_string +=
std::string(
"dummy payment ID");
6986 memcpy(payment_id.data, payment_id8.data, 8);
6987 memset(payment_id.data + 8, 0, 24);
6993 if (!payment_id_string.empty())
6994 payment_id_string +=
", ";
6996 payment_id_string +=
" (OBSOLETE)";
7001 for (
size_t s = 0; s < cd.
sources.size(); ++s)
7003 amount += cd.
sources[s].amount;
7004 size_t ring_size = cd.
sources[s].outputs.size();
7005 if (ring_size < min_ring_size)
7006 min_ring_size = ring_size;
7019 auto i = dests.find(entry.
addr);
7020 if (i == dests.end())
7021 dests.insert(std::make_pair(entry.
addr, std::make_pair(
address, entry.
amount)));
7023 i->second.second += entry.
amount;
7024 amount_to_dests += entry.
amount;
7029 if (it == dests.end())
7036 fail_msg_writer() <<
tr(
"Claimed change is larger than payment to the change address");
7041 if (first_known_non_zero_change_index == -1)
7042 first_known_non_zero_change_index = n;
7051 if (it->second.second == 0)
7056 if (payment_id_string.empty())
7057 payment_id_string =
"no payment ID";
7060 size_t n_dummy_outputs = 0;
7061 for (
auto i = dests.begin(); i != dests.end(); )
7063 if (i->second.second > 0)
7065 if (!dest_string.empty())
7066 dest_string +=
", ";
7067 dest_string += (boost::format(
tr(
"sending %s to %s")) %
print_etn(i->second.second) % i->second.first).str();
7073 if (n_dummy_outputs > 0)
7075 if (!dest_string.empty())
7076 dest_string +=
", ";
7079 if (dest_string.empty())
7080 dest_string =
tr(
"with no destinations");
7086 change_string += (boost::format(
tr(
"%s change to %s")) %
print_etn(change) %
address).str();
7089 change_string +=
tr(
"no change");
7091 uint64_t fee = amount - amount_to_dests;
7092 std::string prompt_str = (boost::format(
tr(
"Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay?")) % (
unsigned long)get_num_txes() %
print_etn(amount) %
print_etn(fee) % dest_string % change_string % (
unsigned long)min_ring_size % payment_id_string % extra_message).str();
7100 extra_message = (boost::format(
"%u outputs to import. ") % (unsigned)txs.
transfers.second.size()).str();
7108 extra_message = (boost::format(
"%u key images to import. ") % (unsigned)txs.
key_images.size()).str();
7112 bool simple_wallet::sign_transfer(
const std::vector<std::string> &args_)
7114 if (m_wallet->key_on_device())
7119 if(m_wallet->multisig())
7121 fail_msg_writer() <<
tr(
"This is a multisig wallet, it can only sign with sign_multisig");
7124 if(m_wallet->watch_only())
7129 if (args_.size() > 1 || (args_.size() == 1 && args_[0] !=
"export_raw"))
7136 const bool export_raw = args_.size() == 1;
7138 std::vector<tools::wallet2::pending_tx> ptx;
7141 bool r = m_wallet->sign_tx(
"unsigned_electroneum_tx",
"signed_electroneum_tx", ptx, [&](
const tools::wallet2::unsigned_tx_set &tx){
return accept_loaded_tx(tx); }, export_raw);
7148 catch (
const std::exception &e)
7155 for (
const auto &t: ptx)
7157 if (!txids_as_text.empty())
7158 txids_as_text += (
", ");
7161 success_msg_writer(
true) <<
tr(
"Transaction successfully signed to file ") <<
"signed_electroneum_tx" <<
", txid " << txids_as_text;
7165 for (
size_t i = 0; i < ptx.size(); ++i)
7168 rawfiles_as_text +=
", ";
7169 rawfiles_as_text +=
"signed_electroneum_tx_raw" + (ptx.size() == 1 ?
"" : (
"_" +
std::to_string(i)));
7176 bool simple_wallet::submit_transfer(
const std::vector<std::string> &args_)
7178 if (m_wallet->key_on_device())
7183 if (!try_connect_to_daemon())
7188 std::vector<tools::wallet2::pending_tx> ptx_vector;
7189 bool r = m_wallet->load_tx(
"signed_electroneum_tx", ptx_vector, [&](
const tools::wallet2::signed_tx_set &tx){
return accept_loaded_tx(tx); });
7196 commit_or_save(ptx_vector,
false);
7198 catch (
const std::exception& e)
7200 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
7213 std::vector<std::string> local_args = args_;
7215 if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() !=
hw::device::TREZOR)
7220 if(local_args.size() != 1) {
7235 std::vector<crypto::secret_key> additional_tx_keys;
7237 bool found_tx_key = m_wallet->get_tx_key(txid, tx_key, additional_tx_keys);
7242 for (
size_t i = 0; i < additional_tx_keys.size(); ++i)
7254 bool simple_wallet::set_tx_key(
const std::vector<std::string> &args_)
7256 std::vector<std::string> local_args = args_;
7258 if(local_args.size() != 2) {
7271 std::vector<crypto::secret_key> additional_tx_keys;
7281 local_args[1] = local_args[1].substr(64);
7282 if (local_args[1].
empty())
7284 additional_tx_keys.resize(additional_tx_keys.size() + 1);
7292 catch (
const std::out_of_range &e)
7302 m_wallet->set_tx_key(txid, tx_key, additional_tx_keys);
7305 catch (
const std::exception &e)
7312 bool simple_wallet::get_tx_proof(
const std::vector<std::string> &args)
7314 if (args.size() != 2 && args.size() != 3)
7338 std::string sig_str = m_wallet->get_tx_proof(txid,
info.address,
info.is_subaddress, args.size() == 3 ? args[2] :
"");
7339 const std::string filename =
"electroneum_tx_proof";
7345 catch (
const std::exception &e)
7352 bool simple_wallet::check_tx_key(
const std::vector<std::string> &args_)
7354 std::vector<std::string> local_args = args_;
7356 if(local_args.size() != 3) {
7361 if (!try_connect_to_daemon())
7377 std::vector<crypto::secret_key> additional_tx_keys;
7383 local_args[1] = local_args[1].substr(64);
7384 while (!local_args[1].
empty())
7386 additional_tx_keys.resize(additional_tx_keys.size() + 1);
7392 local_args[1] = local_args[1].substr(64);
7407 m_wallet->check_tx_key(txid, tx_key, additional_tx_keys,
info.address, received, in_pool, confirmations);
7414 success_msg_writer() <<
tr(
"WARNING: this transaction is not yet included in the blockchain!");
7420 success_msg_writer() << boost::format(
tr(
"This transaction has %u confirmations")) % confirmations;
7433 catch (
const std::exception &e)
7442 if(args.size() != 3 && args.size() != 4) {
7447 if (!try_connect_to_daemon())
7479 if (m_wallet->check_tx_proof(txid,
info.address,
info.is_subaddress, args.size() == 4 ? args[3] :
"", sig_str, received, in_pool, confirmations))
7487 success_msg_writer() <<
tr(
"WARNING: this transaction is not yet included in the blockchain!");
7493 success_msg_writer() << boost::format(
tr(
"This transaction has %u confirmations")) % confirmations;
7511 catch (
const std::exception &e)
7518 bool simple_wallet::get_spend_proof(
const std::vector<std::string> &args)
7520 if (m_wallet->key_on_device())
7525 if(args.size() != 1 && args.size() != 2) {
7530 if (m_wallet->watch_only())
7543 if (!try_connect_to_daemon())
7550 const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] :
"");
7551 const std::string filename =
"electroneum_spend_proof";
7557 catch (
const std::exception &e)
7564 bool simple_wallet::check_spend_proof(
const std::vector<std::string> &args)
7566 if(args.size() != 2 && args.size() != 3) {
7578 if (!try_connect_to_daemon())
7590 if (m_wallet->check_spend_proof(txid, args.size() == 3 ? args[2] :
"", sig_str))
7595 catch (
const std::exception& e)
7602 bool simple_wallet::get_reserve_proof(
const std::vector<std::string> &args)
7604 if (m_wallet->key_on_device())
7609 if(args.size() != 1 && args.size() != 2) {
7614 if (m_wallet->watch_only() || m_wallet->multisig())
7616 fail_msg_writer() <<
tr(
"The reserve proof can be generated only by a full wallet");
7620 boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
7621 if (args[0] !=
"all")
7623 account_minreserve = std::pair<uint32_t, uint64_t>();
7624 account_minreserve->first = m_current_subaddress_account;
7632 if (!try_connect_to_daemon())
7639 const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] :
"");
7640 const std::string filename =
"electroneum_reserve_proof";
7646 catch (
const std::exception &e)
7653 bool simple_wallet::check_reserve_proof(
const std::vector<std::string> &args)
7655 if(args.size() != 2 && args.size() != 3) {
7660 if (!try_connect_to_daemon())
7669 if (
info.is_subaddress)
7687 if (m_wallet->check_reserve_proof(
info.address, args.size() == 3 ? args[2] :
"", sig_str, total, spent))
7696 catch (
const std::exception& e)
7703 static std::string get_human_readable_timespan(std::chrono::seconds seconds)
7712 if (ts < 3600 * 24 * 30.5)
7714 if (ts < 3600 * 24 * 365.25)
7716 return sw::tr(
"a long time");
7720 bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vector<transfer_view>& transfers)
7727 bool coinbase =
true;
7732 if (local_args.size() > 0) {
7733 if (local_args[0] ==
"in" || local_args[0] ==
"incoming") {
7735 local_args.erase(local_args.begin());
7737 else if (local_args[0] ==
"out" || local_args[0] ==
"outgoing") {
7738 in = pool = coinbase =
false;
7739 local_args.erase(local_args.begin());
7741 else if (local_args[0] ==
"pending") {
7742 in =
out = failed = coinbase =
false;
7743 local_args.erase(local_args.begin());
7745 else if (local_args[0] ==
"failed") {
7747 local_args.erase(local_args.begin());
7749 else if (local_args[0] ==
"pool") {
7751 local_args.erase(local_args.begin());
7753 else if (local_args[0] ==
"coinbase") {
7756 local_args.erase(local_args.begin());
7758 else if (local_args[0] ==
"all" || local_args[0] ==
"both") {
7759 local_args.erase(local_args.begin());
7764 std::set<uint32_t> subaddr_indices;
7765 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
7767 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
7769 local_args.erase(local_args.begin());
7773 if (local_args.size() > 0 && local_args[0].find(
'=') == std::string::npos) {
7775 min_height = boost::lexical_cast<uint64_t>(local_args[0]);
7777 catch (
const boost::bad_lexical_cast &) {
7781 local_args.erase(local_args.begin());
7785 if (local_args.size() > 0 && local_args[0].find(
'=') == std::string::npos) {
7787 max_height = boost::lexical_cast<uint64_t>(local_args[0]);
7789 catch (
const boost::bad_lexical_cast &) {
7793 local_args.erase(local_args.begin());
7796 const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
7798 if (in || coinbase) {
7799 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
7800 m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7801 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7806 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7807 payment_id = payment_id.substr(0,16);
7815 locked_msg =
"locked";
7820 if (bh >= last_block_height)
7831 transfers.push_back({
7850 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
7851 m_wallet->get_payments_out(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7852 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7856 std::vector<std::pair<std::string, uint64_t>> destinations;
7861 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7862 payment_id = payment_id.substr(0,16);
7864 transfers.push_back({
7885 m_in_manual_refresh.store(
true, std::memory_order_relaxed);
7888 m_wallet->update_pool_state();
7889 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
7890 m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
7891 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7894 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7895 payment_id = payment_id.substr(0,16);
7899 if (i->second.m_double_spend_seen)
7900 double_spend_note =
tr(
"[Double spend seen on the network: this transaction may or may not end up being mined] ");
7901 if (i->second.m_nonexistent_utxo_seen)
7902 success_msg_writer() <<
tr(
"Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
7903 transfers.push_back({
7915 note + double_spend_note,
7920 catch (
const std::exception& e)
7928 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
7929 m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account, subaddr_indices);
7930 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
7934 std::vector<std::pair<std::string, uint64_t>> destinations;
7935 for (
const auto &d: pd.
m_dests) {
7939 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7940 payment_id = payment_id.substr(0,16);
7943 if ((failed && is_failed) || (!is_failed &&
pending)) {
7944 transfers.push_back({
7945 (is_failed ?
"failed" :
"pending"),
7946 (is_failed ?
"failed" :
"pending"),
7963 std::sort(transfers.begin(), transfers.end(), [](
const transfer_view&
a,
const transfer_view& b) ->
bool {
7964 if (a.confirmed && !b.confirmed)
7966 if (a.block == b.block)
7967 return a.timestamp < b.timestamp;
7968 return a.block < b.block;
7974 bool simple_wallet::show_transfers(
const std::vector<std::string> &args_)
7976 std::vector<std::string> local_args = args_;
7978 if(local_args.size() > 4) {
7979 fail_msg_writer() <<
tr(
"usage: show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
7985 std::vector<transfer_view> all_transfers;
7987 if (!get_transfers(local_args, all_transfers))
7992 for (
const auto& transfer : all_transfers)
7997 if (!transfer.outputs.empty())
8000 for (
const auto& output : transfer.outputs)
8002 if (!destinations.empty())
8003 destinations +=
", ";
8004 destinations += (transfer.direction ==
"in" ? output.first.substr(0, 6) : output.first) +
":" +
print_etn(output.second);
8008 auto formatter = boost::format(
"%8.8llu %6.6s %8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s");
8010 message_writer(color,
false) << formatter
8012 % transfer.direction
8017 % transfer.payment_id
8020 % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](
uint32_t i) { return std::to_string(i); }),
", ")
8027 bool simple_wallet::export_transfers(
const std::vector<std::string>& args_)
8029 std::vector<std::string> local_args = args_;
8031 if(local_args.size() > 5) {
8032 fail_msg_writer() <<
tr(
"usage: export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]");
8038 std::vector<transfer_view> all_transfers;
8041 if (!get_transfers(local_args, all_transfers))
8045 std::string filename = (boost::format(
"output%u.csv") % m_current_subaddress_account).str();
8046 if (local_args.size() > 0 && local_args[0].substr(0, 7) ==
"output=")
8048 filename = local_args[0].substr(7, -1);
8049 local_args.erase(local_args.begin());
8052 std::ofstream file(filename);
8056 boost::format(
"%8.8s,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s") %
8057 tr(
"block") %
tr(
"direction") %
tr(
"unlocked") %
tr(
"timestamp") %
tr(
"amount") %
tr(
"running balance") %
tr(
"hash") %
tr(
"payment ID") %
tr(
"fee") %
tr(
"destination") %
tr(
"amount") %
tr(
"index") %
tr(
"note")
8061 auto formatter = boost::format(
"%8.8llu,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s");
8063 for (
const auto& transfer : all_transfers)
8066 if (transfer.confirmed)
8068 if (transfer.direction ==
"in" || transfer.direction ==
"block")
8069 running_balance += transfer.amount;
8071 running_balance -= transfer.amount + transfer.fee;
8076 % transfer.direction
8082 % transfer.payment_id
8084 % (transfer.outputs.size() ? transfer.outputs[0].first :
"-")
8085 % (transfer.outputs.size() ?
print_etn(transfer.outputs[0].second) :
"")
8090 for (
size_t i = 1; i < transfer.outputs.size(); ++i)
8102 % transfer.outputs[i].first
8116 bool simple_wallet::unspent_outputs(
const std::vector<std::string> &args_)
8118 if(args_.size() > 3)
8123 auto local_args = args_;
8125 std::set<uint32_t> subaddr_indices;
8126 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
8128 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
8130 local_args.erase(local_args.begin());
8134 uint64_t max_amount = std::numeric_limits<uint64_t>::max();
8135 if (local_args.size() > 0)
8142 local_args.erase(local_args.begin());
8143 if (local_args.size() > 0)
8150 local_args.erase(local_args.begin());
8152 if (min_amount > max_amount)
8159 m_wallet->get_transfers(transfers);
8160 std::map<uint64_t, tools::wallet2::transfer_container> amount_to_tds;
8161 uint64_t min_height = std::numeric_limits<uint64_t>::max();
8163 uint64_t found_min_amount = std::numeric_limits<uint64_t>::max();
8166 for (
const auto& td : transfers)
8171 amount_to_tds[amount].push_back(td);
8174 if (found_min_amount > amount) found_min_amount = amount;
8175 if (found_max_amount < amount) found_max_amount = amount;
8178 if (amount_to_tds.empty())
8183 for (
const auto& amount_tds : amount_to_tds)
8185 auto& tds = amount_tds.second;
8187 for (
size_t i = 0; i < tds.size(); )
8189 std::ostringstream oss;
8190 for (
size_t j = 0; j < 8 && i < tds.size(); ++i, ++j)
8191 oss << tds[i].m_block_height <<
tr(
" ");
8196 <<
tr(
"\nMin block height: ") << min_height
8197 <<
tr(
"\nMax block height: ") << max_height
8198 <<
tr(
"\nMin amount found: ") <<
print_etn(found_min_amount)
8199 <<
tr(
"\nMax amount found: ") <<
print_etn(found_max_amount)
8200 <<
tr(
"\nTotal count: ") <<
count;
8201 const size_t histogram_height = 10;
8202 const size_t histogram_width = 50;
8203 double bin_size = (max_height - min_height + 1.0) / histogram_width;
8204 size_t max_bin_count = 0;
8205 std::vector<size_t> histogram(histogram_width, 0);
8206 for (
const auto& amount_tds : amount_to_tds)
8208 for (
auto& td : amount_tds.second)
8211 if (bin_index >= histogram_width)
8212 bin_index = histogram_width - 1;
8213 histogram[bin_index]++;
8214 if (max_bin_count < histogram[bin_index])
8215 max_bin_count = histogram[bin_index];
8218 for (
size_t x = 0; x < histogram_width; ++x)
8220 double bin_count = histogram[x];
8221 if (max_bin_count > histogram_height)
8222 bin_count *= histogram_height / (double)max_bin_count;
8223 if (histogram[x] > 0 && bin_count < 1.0)
8225 histogram[x] = bin_count;
8227 std::vector<std::string> histogram_line(histogram_height,
std::string(histogram_width,
' '));
8228 for (
size_t y = 0; y < histogram_height; ++y)
8230 for (
size_t x = 0; x < histogram_width; ++x)
8232 if (y < histogram[x])
8233 histogram_line[y][x] =
'*';
8236 double count_per_star = max_bin_count / (double)histogram_height;
8237 if (count_per_star < 1)
8240 <<
tr(
"\nBin size: ") << bin_size
8241 <<
tr(
"\nOutputs per *: ") << count_per_star;
8242 ostringstream histogram_str;
8243 histogram_str <<
tr(
"count\n ^\n");
8244 for (
size_t y = histogram_height; y > 0; --y)
8245 histogram_str <<
tr(
" |") << histogram_line[y - 1] <<
tr(
"|\n");
8247 <<
tr(
" +") <<
std::string(histogram_width,
'-') <<
tr(
"+--> block height\n")
8249 <<
tr(
" ") << min_height <<
std::string(histogram_width - 8,
' ') << max_height;
8254 bool simple_wallet::rescan_blockchain(
const std::vector<std::string> &args_)
8257 ResetType reset_type = ResetSoft;
8261 if (args_[0] ==
"hard")
8263 reset_type = ResetHard;
8265 else if (args_[0] ==
"soft")
8267 reset_type = ResetSoft;
8269 else if (args_[0] ==
"keep_ki")
8271 reset_type = ResetSoftKeepKI;
8279 if (args_.size() > 1)
8283 start_height = boost::lexical_cast<uint64_t>( args_[1] );
8285 catch(
const boost::bad_lexical_cast &)
8292 if (reset_type == ResetHard)
8294 message_writer() <<
tr(
"Warning: this will lose any information which can not be recovered from the blockchain.");
8295 message_writer() <<
tr(
"This includes destination addresses, tx secret keys, tx notes, etc");
8296 std::string confirm = input_line(
tr(
"Rescan anyway?"),
true);
8304 const uint64_t wallet_from_height = m_wallet->get_refresh_from_block_height();
8305 if (start_height > wallet_from_height)
8307 message_writer() <<
tr(
"Warning: your restore height is higher than wallet restore height: ") << wallet_from_height;
8308 std::string confirm = input_line(
tr(
"Rescan anyway ? (Y/Yes/N/No): "));
8316 return refresh_main(start_height, reset_type,
true);
8319 void simple_wallet::check_for_messages()
8323 std::vector<mms::message> new_messages;
8324 bool new_message = get_message_store().
check_for_messages(get_multisig_wallet_state(), new_messages);
8328 list_mms_messages(new_messages);
8335 void simple_wallet::wallet_idle_thread()
8339 boost::unique_lock<boost::mutex> lock(m_idle_mutex);
8340 if (!m_idle_run.load(std::memory_order_relaxed))
8344 if (m_auto_refresh_enabled)
8346 m_auto_refresh_refreshing =
true;
8351 if (try_connect_to_daemon(
true))
8352 m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, fetched_blocks, received_etn,
false);
8355 m_auto_refresh_refreshing =
false;
8361 if (m_auto_refresh_enabled && get_message_store().get_active())
8363 check_for_messages();
8366 if (!m_idle_run.load(std::memory_order_relaxed))
8368 m_idle_cond.wait_for(lock, boost::chrono::seconds(90));
8374 std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
8376 if (!m_wallet->check_connection(NULL))
8377 prompt +=
tr(
" (no daemon)");
8378 else if (!m_wallet->is_synced())
8379 prompt +=
tr(
" (out of sync)");
8387 try_connect_to_daemon();
8390 refresh_main(0, ResetNone,
true);
8392 m_auto_refresh_enabled = m_wallet->auto_refresh();
8395 m_idle_thread = boost::thread([&]{wallet_idle_thread();});
8400 return m_cmd_binder.
run_handling([
this](){
return get_prompt();},
"");
8408 bool simple_wallet::account(
const std::vector<std::string> &args)
8427 std::vector<std::string> local_args = args;
8429 local_args.erase(local_args.begin());
8430 if (command ==
"new")
8435 label =
tr(
"(Untitled account)");
8436 m_wallet->add_subaddress_account(label);
8437 m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8442 else if(command ==
"generate" && local_args.size() == 1)
8457 uint64_t startAccountsCount = m_wallet->get_num_subaddress_accounts();
8460 m_wallet->add_subaddress_account(
"(Untitled account " +
std::to_string(startAccountsCount + i+1) +
" )",
false);
8463 success_msg_writer() <<
"Generating Accounts... (" << startAccountsCount + i+1 <<
"/" << startAccountsCount+
count <<
")";
8466 m_wallet->add_subaddress_account(
"(Untitled account " +
std::to_string(startAccountsCount +
count) +
" )",
true);
8468 m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8470 catch (
const std::exception& e)
8476 else if (command ==
"switch" && local_args.size() == 1)
8485 if (index_major >= m_wallet->get_num_subaddress_accounts())
8487 fail_msg_writer() <<
tr(
"specify an index between 0 and ") << (m_wallet->get_num_subaddress_accounts() - 1);
8490 m_current_subaddress_account = index_major;
8494 else if (command ==
"label" && local_args.size() >= 1)
8503 local_args.erase(local_args.begin());
8507 m_wallet->set_subaddress_label({index_major, 0}, label);
8511 catch (
const std::exception& e)
8516 else if (command ==
"tag" && local_args.size() >= 2)
8519 std::set<uint32_t> account_indices;
8520 for (
size_t i = 1; i < local_args.size(); ++i)
8528 account_indices.insert(account_index);
8532 m_wallet->set_account_tag(account_indices, tag);
8533 print_accounts(tag);
8535 catch (
const std::exception& e)
8540 else if (command ==
"untag" && local_args.size() >= 1)
8542 std::set<uint32_t> account_indices;
8543 for (
size_t i = 0; i < local_args.size(); ++i)
8551 account_indices.insert(account_index);
8555 m_wallet->set_account_tag(account_indices,
"");
8558 catch (
const std::exception& e)
8563 else if (command ==
"tag_description" && local_args.size() >= 1)
8567 if (local_args.size() > 1)
8569 local_args.erase(local_args.begin());
8570 description = boost::join(local_args,
" ");
8574 m_wallet->set_account_tag_description(tag, description);
8575 print_accounts(tag);
8577 catch (
const std::exception& e)
8589 void simple_wallet::print_accounts()
8591 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8592 size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
8593 for (
const std::pair<std::string, std::string>& p : account_tags.first)
8596 print_accounts(tag);
8597 num_untagged_accounts -=
std::count(account_tags.second.begin(), account_tags.second.end(), tag);
8601 if (num_untagged_accounts > 0)
8604 if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) {
8605 if (m_wallet->balance_all(
false)) {
8607 <<
tr(
", pre v10 unlocked balance: ")
8608 <<
print_etn(m_wallet->unlocked_balance_all(
false));
8611 <<
tr(
", unlocked balance: ") <<
print_etn(m_wallet->unlocked_balance_all(
true));
8615 void simple_wallet::print_accounts(
const std::string& tag)
8617 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8624 if (account_tags.first.count(tag) == 0)
8633 uint64_t total_balance = 0, total_unlocked_balance = 0, total_balance_public_chain = 0, total_unlocked_balance_public_chain = 0;
8634 bool found_old_bal =
false;
8636 for (
uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8637 if (account_tags.second[account_index] != tag)
8639 if (m_wallet->balance(account_index,
false)) {
8640 found_old_bal =
true;
8645 if (found_old_bal) {
8648 tr(
"Pre V10 Unlocked balance") %
tr(
"Label");
8649 for (
uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8650 if (account_tags.second[account_index] != tag)
8653 % (m_current_subaddress_account == account_index ?
'*' :
' ')
8655 % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8656 %
print_etn(m_wallet->balance(account_index,
false))
8657 %
print_etn(m_wallet->unlocked_balance(account_index,
false))
8658 % m_wallet->get_subaddress_label({account_index, 0});
8659 total_balance += m_wallet->balance(account_index,
false);
8660 total_unlocked_balance += m_wallet->unlocked_balance(account_index,
false);
8663 <<
tr(
"----------------------------------------------------------------------------------");
8670 if(m_wallet->synced_to_v10()) {
8673 tr(
"Unlocked balance") %
tr(
"Label");
8674 for (
uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8675 if (account_tags.second[account_index] != tag)
8678 % (m_current_subaddress_account == account_index ?
'*' :
' ')
8680 % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8681 %
print_etn(m_wallet->balance(account_index,
true))
8682 %
print_etn(m_wallet->unlocked_balance(account_index,
true))
8683 % m_wallet->get_subaddress_label({account_index, 0});
8684 total_balance_public_chain += m_wallet->balance(account_index,
true);
8685 total_unlocked_balance_public_chain += m_wallet->unlocked_balance(account_index,
true);
8688 <<
tr(
"----------------------------------------------------------------------------------");
8690 print_etn(total_unlocked_balance_public_chain);
8695 bool simple_wallet::print_address(
const std::vector<std::string> &args)
8704 std::vector<std::string> local_args = args;
8706 m_wallet->get_transfers(transfers);
8708 auto print_address_sub = [
this, &transfers](
uint32_t index)
8710 bool used = std::find_if(
8711 transfers.begin(), transfers.end(),
8713 return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
8714 }) != transfers.end();
8715 success_msg_writer() << index <<
" " << m_wallet->get_subaddress_as_str({m_current_subaddress_account, index}) <<
" " << (index == 0 ?
tr(
"Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) <<
" " << (used ?
tr(
"(used)") :
"");
8719 if (local_args.empty())
8721 print_address_sub(index);
8723 else if (local_args.size() == 1 && local_args[0] ==
"all")
8725 local_args.erase(local_args.begin());
8726 for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
8727 print_address_sub(index);
8729 else if (local_args[0] ==
"new")
8731 local_args.erase(local_args.begin());
8733 if (local_args.size() > 0)
8734 label = boost::join(local_args,
" ");
8736 label =
tr(
"(Untitled address)");
8737 m_wallet->add_subaddress(m_current_subaddress_account, label);
8738 print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8740 else if (local_args.size() >= 2 && local_args[0] ==
"label")
8747 if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8749 fail_msg_writer() <<
tr(
"specify an index between 0 and ") << (m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8752 local_args.erase(local_args.begin());
8753 local_args.erase(local_args.begin());
8755 m_wallet->set_subaddress_label({m_current_subaddress_account, index}, label);
8756 print_address_sub(index);
8760 local_args.erase(local_args.begin());
8763 if (local_args.size() > 0)
8770 local_args.erase(local_args.begin());
8772 if (index_max < index_min)
8773 std::swap(index_min, index_max);
8774 if (index_min >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8779 if (index_max >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8781 message_writer() <<
tr(
"<index_max> exceeds the bound");
8782 index_max = m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1;
8784 for (index = index_min; index <= index_max; ++index)
8785 print_address_sub(index);
8795 bool simple_wallet::print_integrated_address(
const std::vector<std::string> &args)
8798 if (args.size() > 1)
8803 if (args.size() == 0)
8805 if (m_current_subaddress_account != 0)
8807 fail_msg_writer() <<
tr(
"Integrated addresses can only be created for account 0");
8810 payment_id = crypto::rand<crypto::hash8>();
8812 success_msg_writer() <<
tr(
"Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8817 if (m_current_subaddress_account != 0)
8819 fail_msg_writer() <<
tr(
"Integrated addresses can only be created for account 0");
8822 success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8829 if (
info.has_payment_id)
8845 bool simple_wallet::address_book(
const std::vector<std::string> &args)
8847 if (args.size() == 0)
8850 else if (args.size() == 1 || (args[0] !=
"add" && args[0] !=
"delete"))
8855 else if (args[0] ==
"add")
8864 size_t description_start = 2;
8865 if (
info.has_payment_id)
8867 memcpy(payment_id.data,
info.payment_id.data, 8);
8869 else if (!
info.has_payment_id && args.size() >= 4 && args[2] ==
"pid")
8874 description_start += 2;
8878 fail_msg_writer() <<
tr(
"Short payment IDs are to be used within an integrated address only");
8888 for (
size_t i = description_start; i < args.size(); ++i)
8890 if (i > description_start)
8892 description += args[i];
8894 m_wallet->add_address_book_row(
info.address, payment_id, description,
info.is_subaddress);
8904 m_wallet->delete_address_book_row(row_id);
8906 auto address_book = m_wallet->get_address_book();
8907 if (address_book.empty())
8913 for (
size_t i = 0; i < address_book.size(); ++i) {
8914 auto& row = address_book[i];
8924 bool simple_wallet::set_tx_note(
const std::vector<std::string> &args)
8926 if (args.size() == 0)
8941 for (
size_t n = 1; n < args.size(); ++n)
8947 m_wallet->set_tx_note(txid, note);
8952 bool simple_wallet::get_tx_note(
const std::vector<std::string> &args)
8954 if (args.size() != 1)
8977 bool simple_wallet::set_description(
const std::vector<std::string> &args)
8982 for (
size_t n = 0; n < args.size(); ++n)
8986 description += args[n];
8988 m_wallet->set_description(description);
8993 bool simple_wallet::get_description(
const std::vector<std::string> &args)
8995 if (args.size() != 0)
9001 std::string description = m_wallet->get_description();
9002 if (description.empty())
9010 bool simple_wallet::status(
const std::vector<std::string> &args)
9012 uint64_t local_height = m_wallet->get_blockchain_current_height();
9015 if (!m_wallet->check_connection(&
version, &ssl))
9022 uint64_t bc_height = get_daemon_blockchain_height(err);
9025 bool synced = local_height == bc_height;
9026 success_msg_writer() <<
"Refreshed " << local_height <<
"/" << bc_height <<
", " << (synced ?
"synced" :
"syncing")
9027 <<
", daemon RPC v" << get_version_string(
version) <<
", " << (ssl ?
"SSL" :
"no SSL");
9031 fail_msg_writer() <<
"Refreshed " << local_height <<
"/?, daemon connection error";
9036 bool simple_wallet::wallet_info(
const std::vector<std::string> &args)
9040 std::string description = m_wallet->get_description();
9041 if (description.empty())
9043 description =
"<Not set>";
9045 message_writer() <<
tr(
"Filename: ") << m_wallet->get_wallet_file();
9046 message_writer() <<
tr(
"Description: ") << description;
9047 message_writer() <<
tr(
"Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
9049 if (m_wallet->watch_only())
9050 type =
tr(
"Watch only");
9051 else if (m_wallet->multisig(&ready, &
threshold, &total))
9052 type = (boost::format(
tr(
"%u/%u multisig%s")) %
threshold % total % (ready ?
"" :
" (not yet finalized)")).str();
9054 type =
tr(
"Normal");
9055 message_writer() <<
tr(
"Type: ") << type;
9056 message_writer() <<
tr(
"Network type: ") << (
9062 bool simple_wallet::sign(
const std::vector<std::string> &args)
9064 if (m_wallet->key_on_device())
9069 if (args.size() != 1)
9074 if (m_wallet->watch_only())
9079 if (m_wallet->multisig())
9101 bool simple_wallet::verify(
const std::vector<std::string> &args)
9103 if (args.size() != 3)
9139 bool simple_wallet::export_key_images(
const std::vector<std::string> &args)
9141 if (m_wallet->key_on_device())
9146 if (args.size() != 1)
9151 if (m_wallet->watch_only())
9158 if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9165 if (!m_wallet->export_key_images(filename))
9171 catch (
const std::exception &e)
9173 LOG_ERROR(
"Error exporting key images: " << e.what());
9182 bool simple_wallet::import_key_images(
const std::vector<std::string> &args)
9184 if (m_wallet->key_on_device())
9189 if (!m_wallet->is_trusted_daemon())
9191 fail_msg_writer() <<
tr(
"this command requires a trusted daemon. Enable with --trusted-daemon");
9195 if (args.size() != 1)
9206 uint64_t height = m_wallet->import_key_images(filename, spent, unspent);
9210 catch (
const std::exception &e)
9219 bool simple_wallet::hw_key_images_sync(
const std::vector<std::string> &args)
9221 if (!m_wallet->key_on_device())
9226 if (!m_wallet->get_account().get_device().has_ki_cold_sync())
9233 key_images_sync_intern();
9237 void simple_wallet::key_images_sync_intern(){
9240 message_writer(
console_color_white,
false) <<
tr(
"Please confirm the key image sync on the device");
9247 if (!m_wallet->is_trusted_daemon())
9249 message_writer() <<
tr(
"Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
9259 catch (
const std::exception &e)
9265 bool simple_wallet::hw_reconnect(
const std::vector<std::string> &args)
9267 if (!m_wallet->key_on_device())
9276 bool r = m_wallet->reconnect_device();
9281 catch (
const std::exception &e)
9290 bool simple_wallet::export_outputs(
const std::vector<std::string> &args)
9292 if (m_wallet->key_on_device())
9297 if (args.size() != 1)
9304 if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9311 std::string data = m_wallet->export_outputs_to_str();
9319 catch (
const std::exception &e)
9321 LOG_ERROR(
"Error exporting outputs: " << e.what());
9330 bool simple_wallet::import_outputs(
const std::vector<std::string> &args)
9332 if (m_wallet->key_on_device())
9337 if (args.size() != 1)
9355 size_t n_outputs = m_wallet->import_outputs_from_str(data);
9356 success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) <<
" outputs imported";
9358 catch (
const std::exception &e)
9360 fail_msg_writer() <<
"Failed to import outputs " << filename <<
": " << e.what();
9367 bool simple_wallet::show_transfer(
const std::vector<std::string> &args)
9369 if (args.size() != 1)
9383 const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
9385 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
9386 m_wallet->get_payments(payments, 0, (
uint64_t)-1, m_current_subaddress_account);
9387 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
9391 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9392 payment_id = payment_id.substr(0,16);
9402 uint64_t last_block_reward = m_wallet->get_last_block_reward();
9403 uint64_t suggested_threshold = last_block_reward ? (pd.
m_amount + last_block_reward - 1) / last_block_reward : 0;
9404 if (bh >= last_block_height)
9405 success_msg_writer() <<
"Locked: " << (bh - last_block_height) <<
" blocks to unlock";
9406 else if (suggested_threshold > 0)
9426 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
9427 m_wallet->get_payments_out(payments_out, 0, (
uint64_t)-1, m_current_subaddress_account);
9428 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
9429 if (i->first == txid)
9441 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9442 payment_id = payment_id.substr(0,16);
9459 m_wallet->update_pool_state();
9460 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
9461 m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account);
9462 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
9467 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9468 payment_id = payment_id.substr(0,16);
9476 if (i->second.m_double_spend_seen)
9477 success_msg_writer() <<
tr(
"Double spend seen on the network: this transaction may or may not end up being mined");
9478 if (i->second.m_nonexistent_utxo_seen)
9479 success_msg_writer() <<
tr(
"Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
9489 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
9490 m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account);
9491 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
9492 if (i->first == txid)
9498 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9499 payment_id = payment_id.substr(0,16);
9502 success_msg_writer() << (is_failed ?
"Failed" :
"Pending") <<
" outgoing transaction found";
9525 if (m_in_manual_refresh.load(std::memory_order_relaxed))
9535 void simple_wallet::commit_or_save(std::vector<tools::wallet2::pending_tx>& ptx_vector,
bool do_not_relay)
9538 while (!ptx_vector.empty())
9540 auto & ptx = ptx_vector.back();
9549 success_msg_writer(
true) <<
tr(
"Transaction successfully saved to ") << filename <<
tr(
", txid ") << txid;
9551 fail_msg_writer() <<
tr(
"Failed to save transaction to ") << filename <<
tr(
", txid ") << txid;
9555 m_wallet->commit_tx(ptx);
9557 <<
tr(
"You can check its status by using the `show_transfers` command.");
9560 ptx_vector.pop_back();
9570 std::locale::global(boost::locale::generator().generate(
""));
9571 boost::filesystem::path::imbue(std::locale());
9574 po::options_description desc_params(
wallet_args::tr(
"Wallet options"));
9602 po::positional_options_description positional_options;
9605 boost::optional<po::variables_map> vm;
9606 bool should_terminate =
false;
9609 "electroneum-wallet-cli [--wallet-file=<filename>|--generate-new-wallet=<filename>] [<COMMAND>]",
9610 sw::tr(
"This is the command line electroneum wallet. It needs to connect to a electroneum\ndaemon to work correctly.\nWARNING: Do not reuse your Electroneum keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."),
9614 "electroneum-wallet-cli.log"
9622 if (should_terminate)
9630 const bool r = w.
init(*vm);
9634 if (!command.empty())
9650 if (type == CTRL_C_EVENT)
9688 bool simple_wallet::user_confirms(
const std::string &question)
9690 std::string answer = input_line(question +
tr(
" (Y/Yes/N/No): "));
9699 number = boost::lexical_cast<uint32_t>(arg);
9700 valid = (number >= lower_bound) && (number <= upper_bound);
9702 catch(
const boost::bad_lexical_cast &)
9708 bool simple_wallet::choose_mms_processing(
const std::vector<mms::processing_data> &data_list,
uint32_t &choice)
9710 size_t choices = data_list.size();
9717 message_writer() <<
tr(
"Choose processing:");
9719 for (
size_t i = 0; i < choices; ++i)
9726 text +=
tr(
"Sign tx");
9734 text +=
tr(
"Send the tx for submission to ");
9738 text +=
tr(
"Send the tx for signing to ");
9745 text +=
tr(
"Submit tx");
9748 text +=
tr(
"unknown");
9751 message_writer() << text;
9755 if (std::cin.eof() || line.empty())
9759 bool choice_ok = get_number_from_arg(line, choice, 1, choices);
9771 void simple_wallet::list_mms_messages(
const std::vector<mms::message> &messages)
9773 message_writer() << boost::format(
"%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
tr(
"Id") %
tr(
"I/O") %
tr(
"Authorized Signer")
9774 %
tr(
"Message Type") %
tr(
"Height") %
tr(
"R") %
tr(
"Message State") %
tr(
"Since");
9777 for (
size_t i = 0; i < messages.size(); ++i)
9783 boost::format(
"%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
9795 void simple_wallet::list_signers(
const std::vector<mms::authorized_signer> &signers)
9797 message_writer() << boost::format(
"%2s %-20s %-s") %
tr(
"#") %
tr(
"Label") %
tr(
"Transport Address");
9798 message_writer() << boost::format(
"%2s %-20s %-s") %
"" %
tr(
"Auto-Config Token") %
tr(
"Electroneum Address");
9799 for (
size_t i = 0; i < signers.size(); ++i)
9810 electroneum_address =
tr(
"<not set>");
9813 message_writer() << boost::format(
"%2s %-20s %-s") % (i + 1) % label % transport_address;
9814 message_writer() << boost::format(
"%2s %-20s %-s") %
"" % signer.
auto_config_token % electroneum_address;
9815 message_writer() <<
"";
9819 void simple_wallet::add_signer_config_messages()
9825 const std::vector<mms::authorized_signer> signers = ms.
get_all_signers();
9828 for (
uint32_t i = 1 ; i < num_authorized_signers; ++i)
9834 void simple_wallet::show_message(
const mms::message &m)
9838 bool display_content;
9845 display_content =
true;
9849 display_content =
false;
9852 message_writer() <<
"";
9853 message_writer() <<
tr(
"Message ") << m.
id;
9856 message_writer() <<
tr(
"State: ") << boost::format(
tr(
"%s since %s, %s ago")) %
9860 message_writer() <<
tr(
"Sent: Never");
9864 message_writer() << boost::format(
tr(
"Sent: %s, %s ago")) %
9868 message_writer() <<
tr(
"Content size: ") << m.
content.length() <<
tr(
" bytes");
9869 message_writer() <<
tr(
"Content: ") << (display_content ? sanitized_text :
tr(
"(binary data)"));
9880 void simple_wallet::ask_send_all_ready_messages()
9883 std::vector<mms::message> ready_messages;
9885 for (
size_t i = 0; i < messages.size(); ++i)
9890 ready_messages.push_back(m);
9893 if (ready_messages.size() != 0)
9895 list_mms_messages(ready_messages);
9899 send = user_confirms(
tr(
"Send these messages now?"));
9904 for (
size_t i = 0; i < ready_messages.size(); ++i)
9917 bool valid_id =
false;
9921 id = (
uint32_t)boost::lexical_cast<uint32_t>(arg);
9924 catch (
const boost::bad_lexical_cast &)
9934 void simple_wallet::mms_init(
const std::vector<std::string> &args)
9936 if (args.size() != 3)
9938 fail_msg_writer() <<
tr(
"usage: mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
9944 if (!user_confirms(
tr(
"The MMS is already initialized. Re-initialize by deleting all signer info and messages?")))
9952 std::vector<std::string> numbers;
9953 boost::split(numbers, mn, boost::is_any_of(
"/"));
9954 bool mn_ok = (numbers.size() == 2)
9955 && get_number_from_arg(numbers[1], num_authorized_signers, 2, 100)
9956 && get_number_from_arg(numbers[0], num_required_signers, 2, num_authorized_signers);
9959 fail_msg_writer() <<
tr(
"Error in the number of required signers and/or authorized signers");
9963 ms.
init(get_multisig_wallet_state(), args[1], args[2], num_authorized_signers, num_required_signers);
9966 void simple_wallet::mms_info(
const std::vector<std::string> &args)
9971 message_writer() << boost::format(
"The MMS is active for %s/%s multisig.")
9976 message_writer() <<
tr(
"The MMS is not active.");
9980 void simple_wallet::mms_signer(
const std::vector<std::string> &args)
9983 const std::vector<mms::authorized_signer> &signers = ms.
get_all_signers();
9984 if (args.size() == 0)
9987 list_signers(signers);
10002 if ((args.size() < 2) || (args.size() > 4))
10004 fail_msg_writer() <<
tr(
"mms signer [<number> <label> [<transport_address> [<electroneum_address>]]]");
10008 boost::optional<string> label = args[1];
10009 boost::optional<string> transport_address;
10010 if (args.size() >= 3)
10012 transport_address = args[2];
10014 boost::optional<cryptonote::account_public_address> electroneum_address;
10017 if (args.size() == 4)
10026 electroneum_address =
info.address;
10028 if ((messages.size() > 0) ||
state.multisig)
10030 fail_msg_writer() <<
tr(
"Wallet state does not allow changing Electroneum addresses anymore");
10034 ms.
set_signer(
state, index, label, transport_address, electroneum_address);
10037 void simple_wallet::mms_list(
const std::vector<std::string> &args)
10040 if (args.size() != 0)
10047 list_mms_messages(messages);
10050 void simple_wallet::mms_next(
const std::vector<std::string> &args)
10053 if ((args.size() > 1) || ((args.size() == 1) && (args[0] !=
"sync")))
10058 bool avail =
false;
10059 std::vector<mms::processing_data> data_list;
10060 bool force_sync =
false;
10064 if ((args.size() == 1) && (args[0] ==
"sync"))
10070 string wait_reason;
10076 avail = choose_mms_processing(data_list, choice);
10078 else if (!wait_reason.empty())
10080 message_writer() <<
tr(
"No next step: ") << wait_reason;
10086 bool command_successful =
false;
10090 message_writer() <<
tr(
"prepare_multisig");
10091 command_successful = prepare_multisig_main(std::vector<std::string>(),
true);
10096 message_writer() <<
tr(
"make_multisig");
10097 size_t number_of_key_sets = data.
message_ids.size();
10098 std::vector<std::string> sig_args(number_of_key_sets + 1);
10100 for (
size_t i = 0; i < number_of_key_sets; ++i)
10105 command_successful = make_multisig_main(sig_args,
true);
10111 message_writer() <<
tr(
"exchange_multisig_keys");
10112 size_t number_of_key_sets = data.
message_ids.size();
10114 std::vector<std::string> sig_args(number_of_key_sets);
10115 for (
size_t i = 0; i < number_of_key_sets; ++i)
10120 command_successful = exchange_multisig_keys_main(sig_args,
true);
10126 message_writer() <<
tr(
"export_multisig_info");
10127 std::vector<std::string> export_args;
10128 export_args.push_back(
"MMS");
10129 command_successful = export_multisig_main(export_args,
true);
10135 message_writer() <<
tr(
"import_multisig_info");
10136 std::vector<std::string> import_args;
10137 for (
size_t i = 0; i < data.
message_ids.size(); ++i)
10140 import_args.push_back(m.
content);
10142 command_successful = import_multisig_main(import_args,
true);
10148 message_writer() <<
tr(
"sign_multisig");
10149 std::vector<std::string> sign_args;
10151 sign_args.push_back(m.
content);
10152 command_successful = sign_multisig_main(sign_args,
true);
10158 message_writer() <<
tr(
"submit_multisig");
10159 std::vector<std::string> submit_args;
10161 submit_args.push_back(m.
content);
10162 command_successful = submit_multisig_main(submit_args,
true);
10168 message_writer() <<
tr(
"Send tx");
10173 command_successful =
true;
10179 message_writer() <<
tr(
"Process signer config");
10188 std::vector<mms::authorized_signer> signers;
10190 list_signers(signers);
10191 if (!user_confirms(
tr(
"Replace current signer config with the one displayed above?")))
10199 command_successful =
true;
10205 message_writer() <<
tr(
"Process auto config data");
10207 for (
size_t i = 0; i < data.
message_ids.size(); ++i)
10213 add_signer_config_messages();
10214 command_successful =
true;
10219 message_writer() <<
tr(
"Nothing ready to process");
10223 if (command_successful)
10228 ask_send_all_ready_messages();
10234 void simple_wallet::mms_sync(
const std::vector<std::string> &args)
10237 if (args.size() != 0)
10246 message_writer() <<
tr(
"export_multisig_info");
10247 std::vector<std::string> export_args;
10248 export_args.push_back(
"MMS");
10249 export_multisig_main(export_args,
true);
10250 ask_send_all_ready_messages();
10253 void simple_wallet::mms_transfer(
const std::vector<std::string> &args)
10256 transfer_main(
Transfer, args,
true);
10259 void simple_wallet::mms_delete(
const std::vector<std::string> &args)
10261 if (args.size() != 1)
10268 if (args[0] ==
"all")
10270 if (user_confirms(
tr(
"Delete all messages?")))
10278 bool valid_id = get_message_from_arg(args[0], m);
10287 void simple_wallet::mms_send(
const std::vector<std::string> &args)
10289 if (args.size() == 0)
10291 ask_send_all_ready_messages();
10294 else if (args.size() != 1)
10302 bool valid_id = get_message_from_arg(args[0], m);
10309 void simple_wallet::mms_receive(
const std::vector<std::string> &args)
10311 if (args.size() != 0)
10316 std::vector<mms::message> new_messages;
10322 list_mms_messages(new_messages);
10326 void simple_wallet::mms_export(
const std::vector<std::string> &args)
10328 if (args.size() != 1)
10336 bool valid_id = get_message_from_arg(args[0], m);
10339 const std::string filename =
"mms_message_content";
10351 void simple_wallet::mms_note(
const std::vector<std::string> &args)
10354 if (args.size() == 0)
10358 for (
size_t i = 0; i < messages.size(); ++i)
10368 if (args.size() < 2)
10381 for (
size_t n = 1; n < args.size(); ++n)
10392 ask_send_all_ready_messages();
10395 void simple_wallet::mms_show(
const std::vector<std::string> &args)
10397 if (args.size() != 1)
10405 bool valid_id = get_message_from_arg(args[0], m);
10412 void simple_wallet::mms_set(
const std::vector<std::string> &args)
10414 bool set = args.size() == 2;
10415 bool query = args.size() == 1;
10416 if (!set && !query)
10423 if (args[0] ==
"auto-send")
10428 bool ok = parse_bool(args[1], result);
10440 message_writer() << (ms.
get_auto_send() ?
tr(
"Auto-send is on") :
tr(
"Auto-send is off"));
10449 void simple_wallet::mms_help(
const std::vector<std::string> &args)
10451 if (args.size() > 1)
10456 std::vector<std::string> help_args;
10457 help_args.push_back(
"mms");
10458 if (args.size() == 1)
10460 help_args.push_back(args[0]);
10465 void simple_wallet::mms_send_signer_config(
const std::vector<std::string> &args)
10467 if (args.size() != 0)
10479 add_signer_config_messages();
10480 ask_send_all_ready_messages();
10483 void simple_wallet::mms_start_auto_config(
const std::vector<std::string> &args)
10487 size_t args_size = args.size();
10488 if ((args_size != 0) && (args_size != other_signers))
10495 fail_msg_writer() <<
tr(
"There are signers without a label set. Complete labels before auto-config or specify them as parameters here.");
10501 if (!user_confirms(
tr(
"Auto-config is already running. Cancel and restart?")))
10508 if (args_size != 0)
10511 for (
uint32_t i = 1; i < (other_signers + 1); ++i)
10521 void simple_wallet::mms_stop_auto_config(
const std::vector<std::string> &args)
10523 if (args.size() != 0)
10528 if (!user_confirms(
tr(
"Delete any auto-config tokens and stop auto-config?")))
10537 void simple_wallet::mms_auto_config(
const std::vector<std::string> &args)
10539 if (args.size() != 1)
10554 if (!user_confirms(
tr(
"Auto-config already running. Cancel and restart?")))
10561 ask_send_all_ready_messages();
10564 bool simple_wallet::mms(
const std::vector<std::string> &args)
10568 m_wallet->get_multisig_wallet_state();
10570 catch(
const std::exception &e)
10579 if (args.size() == 0)
10586 std::vector<std::string> mms_args = args;
10587 mms_args.erase(mms_args.begin());
10589 if (sub_command ==
"init")
10591 mms_init(mms_args);
10596 fail_msg_writer() <<
tr(
"The MMS is not active. Activate using the \"mms init\" command");
10599 else if (sub_command ==
"info")
10601 mms_info(mms_args);
10603 else if (sub_command ==
"signer")
10605 mms_signer(mms_args);
10607 else if (sub_command ==
"list")
10609 mms_list(mms_args);
10611 else if (sub_command ==
"next")
10613 mms_next(mms_args);
10615 else if (sub_command ==
"sync")
10617 mms_sync(mms_args);
10619 else if (sub_command ==
"transfer")
10621 mms_transfer(mms_args);
10623 else if (sub_command ==
"delete")
10625 mms_delete(mms_args);
10627 else if (sub_command ==
"send")
10629 mms_send(mms_args);
10631 else if (sub_command ==
"receive")
10633 mms_receive(mms_args);
10635 else if (sub_command ==
"export")
10637 mms_export(mms_args);
10639 else if (sub_command ==
"note")
10641 mms_note(mms_args);
10643 else if (sub_command ==
"show")
10645 mms_show(mms_args);
10647 else if (sub_command ==
"set")
10651 else if (sub_command ==
"help")
10653 mms_help(mms_args);
10655 else if (sub_command ==
"send_signer_config")
10657 mms_send_signer_config(mms_args);
10659 else if (sub_command ==
"start_auto_config")
10661 mms_start_auto_config(mms_args);
10663 else if (sub_command ==
"stop_auto_config")
10665 mms_stop_auto_config(mms_args);
10667 else if (sub_command ==
"auto_config")
10669 mms_auto_config(mms_args);
10680 catch (
const std::exception &e)
Manages wallet operations. This is the most abstracted wallet class.
static const char * tr(const char *str)
bool init(const boost::program_options::variables_map &vm)
bool process_command(const std::vector< std::string > &args)
std::vector< uint8_t > extra
std::vector< txin_v > vin
bool process_command_vec(const std::vector< std::string > &cmd)
void set_handler(const std::string &cmd, const callback &hndlr, const std::string &usage="", const std::string &description="")
bool run_handling(std::function< std::string(void)> prompt, const std::string &usage_string, std::function< void(void)> exit_handler=NULL)
bool hex_to_pod(T &pod) const
void split(std::vector< wipeable_string > &fields) const
const char * data() const noexcept
boost::optional< wipeable_string > parse_hexstr() const
bool empty() const noexcept
size_t size() const noexcept
void init(const multisig_wallet_state &state, const std::string &own_label, const std::string &own_transport_address, uint32_t num_authorized_signers, uint32_t num_required_signers)
void process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
bool get_signer_index_by_label(const std::string label, uint32_t &index) const
void process_auto_config_data_message(uint32_t id)
bool check_for_messages(const multisig_wallet_state &state, std::vector< message > &messages)
bool get_auto_send() const
const std::vector< message > & get_all_messages() const
static const char * message_direction_to_string(message_direction direction)
bool get_processable_messages(const multisig_wallet_state &state, bool force_sync, std::vector< processing_data > &data_list, std::string &wait_reason)
void send_message(const multisig_wallet_state &state, uint32_t id)
bool check_auto_config_token(const std::string &raw_token, std::string &adjusted_token) const
size_t add_message(const multisig_wallet_state &state, uint32_t signer_index, message_type type, message_direction direction, const std::string &content)
void delete_all_messages()
bool signer_labels_complete() const
size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token)
void set_message_processed_or_sent(uint32_t id)
void get_signer_config(std::string &signer_config)
std::string signer_to_string(const authorized_signer &signer, uint32_t max_width)
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content)
const std::vector< authorized_signer > & get_all_signers() const
uint32_t get_num_required_signers() const
void set_auto_send(bool auto_send)
void start_auto_config(const multisig_wallet_state &state)
static const char * message_state_to_string(message_state state)
void set_signer(const multisig_wallet_state &state, uint32_t index, const boost::optional< std::string > &label, const boost::optional< std::string > &transport_address, const boost::optional< cryptonote::account_public_address > etn_address)
uint32_t get_num_authorized_signers() const
bool signer_config_complete() const
const authorized_signer & get_signer(uint32_t index) const
void delete_message(uint32_t id)
void unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, std::vector< authorized_signer > &signers)
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const
static const char * message_type_to_string(message_type type)
bool get_message_by_id(uint32_t id, message &m) const
void set_messages_processed(const processing_data &data)
#define MAKE_CORE_RPC_VERSION(major, minor)
#define CORE_RPC_VERSION_MAJOR
#define CORE_RPC_STATUS_OK
#define CORE_RPC_STATUS_BUSY
void sc_reduce32(unsigned char *)
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
#define HF_VERSION_ENFORCE_0_DECOY_TXS
#define DIFFICULTY_TARGET_V6
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
#define HF_VERSION_ENFORCE_0_DECOY_TXS_END
#define CRYPTONOTE_MAX_BLOCK_NUMBER
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
#define HF_VERSION_PER_BYTE_FEE
This is the orginal cryptonote protocol network-events handler, modified by us.
Mnemonic seed generation and wallet restoration from them.
expect< void > success() noexcept
void * memcpy(void *a, const void *b, size_t c)
void verbose(enum verbosity_value level, const char *format,...) ATTR_FORMAT(printf
std::string mlog_get_categories()
void mlog_set_log(const char *log)
void mlog_set_log_level(int level)
#define CATCH_ENTRY_L0(lacation, return_val)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
void save(Archive &a, const std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
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.
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional< public_key > &B, const public_key &D, const signature &sig)
void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant=0, uint64_t height=0)
Holds cryptonote related classes and helpers.
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
unsigned int get_default_decimal_point()
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
const config_t & get_config(network_type nettype)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
const command_line::arg_descriptor< std::string > arg_fallback_to_pow_checkpoint_hash
crypto::hash get_transaction_hash(const transaction &t)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
const command_line::arg_descriptor< uint64_t > arg_fallback_to_pow_checkpoint_height
void set_default_decimal_point(unsigned int decimal_point)
blobdata tx_to_blob(const transaction &tx)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
std::string hex(difficulty_type v)
std::string print_etn(uint64_t amount, unsigned int decimal_point)
bool get_account_address_from_str_or_url(address_parse_info &info, network_type nettype, const std::string &str_or_url, std::function< std::string(const std::string &, const std::vector< std::string > &, bool)> dns_confirm)
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
std::string get_unit(unsigned int decimal_point)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
const command_line::arg_descriptor< std::vector< std::string > > arg_command
const base::type::char_t * unit
@ 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)
std::string to_string(t_connection_type type)
T & unwrap(mlocked< T > &src)
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
mdb_size_t count(MDB_cursor *cur)
error
Tracks LMDB error codes.
@ process_auto_config_data
version
Supported socks variants.
expect< void > send(const epee::span< const std::uint8_t > payload, void *const socket, const int flags) noexcept
const T & move(const T &t)
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
const CharType(& source)[N]
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
for(i=1;i< 1;++i) fe_sq(t0
int main(int argc, char *argv[])
bool parse_priority(const std::string &arg, uint32_t &priority)
#define LONG_PAYMENT_ID_SUPPORT_CHECK()
#define LOCK_IDLE_SCOPE()
#define SCOPED_WALLET_UNLOCK()
std::string join_priority_strings(const char *delimiter)
cryptonote::simple_wallet sw
#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[]
unsigned __int64 uint64_t
uint16_t const RPC_DEFAULT_PORT
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
message_direction direction
std::vector< uint32_t > message_ids
uint32_t receiving_signer_index
message_processing processing
const char *const ELECTRONEUM_RELEASE_NAME
const char *const ELECTRONEUM_VERSION_FULL
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)