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> 78 #include <boost/locale.hpp> 79 #include <boost/filesystem.hpp> 89 using boost::lexical_cast;
90 namespace po = boost::program_options;
93 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 94 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.simplewallet" 96 #define EXTENDED_LOGS_FILE "wallet_details.log" 100 #define MIN_RING_SIZE 1 // Used to inform user about min ring size -- does not track actual protocol 101 #define OUTPUT_EXPORT_FILE_MAGIC "Electroneum output export\003" 103 #define LOCK_IDLE_SCOPE() \ 104 bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ 105 m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ 108 boost::unique_lock<boost::mutex> lock(m_idle_mutex); \ 109 m_idle_cond.notify_all(); \ 110 epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ 111 m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ 114 #define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \ 116 boost::optional<tools::password_container> pwd_container = boost::none; \ 117 if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \ 118 tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); 120 #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) 122 #define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help; 124 #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ 126 if (!m_long_payment_id_support) { \ 127 fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \ 128 fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \ 129 fail_msg_writer() << tr("Use --long-payment-id-support-bad-for-privacy if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ 141 const std::array<const char* const, 5> allowed_priority_strings = {{
"default",
"unimportant",
"normal",
"elevated",
"priority"}};
155 const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {
"allow-mismatched-daemon-version",
sw::tr(
"Allow communicating with a daemon that uses a different RPC version"),
false};
162 const command_line::arg_descriptor<bool> arg_long_payment_id_support = {
"long-payment-id-support-bad-for-privacy",
sw::tr(
"Support obsolete long (unencrypted) payment ids (using them harms your privacy)"),
true};
163 const command_line::arg_descriptor<uint64_t> fallback_to_pow_checkpoint_height = {
"fallback-to-pow-checkpoint-height",
tools::wallet2::tr(
"Warning: This is to set the height for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "), 0,
true};
164 const command_line::arg_descriptor<std::string> fallback_to_pow_checkpoint_hash = {
"fallback-to-pow-checkpoint-hash",
tools::wallet2::tr(
"Warning: This is to set the hash for a custom checkpoint in the event of PoW fallback. Do not use in normal circumstances. See docs for details "),
"",
true};
170 const char* USAGE_START_MINING(
"start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
171 const char* USAGE_SET_DAEMON(
"set_daemon <host>[:<port>] [trusted|untrusted]");
172 const char* USAGE_SHOW_BALANCE(
"balance [detail]");
173 const char* USAGE_INCOMING_TRANSFERS(
"incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
174 const char* USAGE_PAYMENTS(
"payments <PID_1> [<PID_2> ... <PID_N>]");
175 const char* USAGE_PAYMENT_ID(
"payment_id");
176 const char* USAGE_TRANSFER(
"transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
177 const char* USAGE_LOCKED_TRANSFER(
"locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
178 const char* USAGE_LOCKED_SWEEP_ALL(
"locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
179 const char* USAGE_SWEEP_ALL(
"sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
180 const char* USAGE_SWEEP_BELOW(
"sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
181 const char* USAGE_SWEEP_SINGLE(
"sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
182 const char* USAGE_DONATE(
"donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]");
183 const char* USAGE_SIGN_TRANSFER(
"sign_transfer [export_raw]");
184 const char* USAGE_SET_LOG(
"set_log <level>|{+,-,}<categories>");
185 const char* USAGE_ACCOUNT(
"account\n" 186 " account new <label text with white spaces allowed>\n" 187 " account switch <index> \n" 188 " account label <index> <label text with white spaces allowed>\n" 189 " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n" 190 " account untag <account_index_1> [<account_index_2> ...]\n" 191 " account tag_description <tag_name> <description>");
192 const char* USAGE_ADDRESS(
"address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]");
193 const char* USAGE_INTEGRATED_ADDRESS(
"integrated_address [<payment_id> | <address>]");
194 const char* USAGE_ADDRESS_BOOK(
"address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
195 const char* USAGE_SET_VARIABLE(
"set <option> [<value>]");
196 const char* USAGE_GET_TX_KEY(
"get_tx_key <txid>");
197 const char* USAGE_SET_TX_KEY(
"set_tx_key <txid> <tx_key>");
198 const char* USAGE_CHECK_TX_KEY(
"check_tx_key <txid> <txkey> <address>");
199 const char* USAGE_GET_TX_PROOF(
"get_tx_proof <txid> <address> [<message>]");
200 const char* USAGE_CHECK_TX_PROOF(
"check_tx_proof <txid> <address> <signature_file> [<message>]");
201 const char* USAGE_GET_SPEND_PROOF(
"get_spend_proof <txid> [<message>]");
202 const char* USAGE_CHECK_SPEND_PROOF(
"check_spend_proof <txid> <signature_file> [<message>]");
203 const char* USAGE_GET_RESERVE_PROOF(
"get_reserve_proof (all|<amount>) [<message>]");
204 const char* USAGE_CHECK_RESERVE_PROOF(
"check_reserve_proof <address> <signature_file> [<message>]");
205 const char* USAGE_SHOW_TRANSFERS(
"show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
206 const char* USAGE_UNSPENT_OUTPUTS(
"unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]");
207 const char* USAGE_RESCAN_BC(
"rescan_bc [hard|soft|keep_ki] [start_height=0]");
208 const char* USAGE_SET_TX_NOTE(
"set_tx_note <txid> [free text note]");
209 const char* USAGE_GET_TX_NOTE(
"get_tx_note <txid>");
210 const char* USAGE_GET_DESCRIPTION(
"get_description");
211 const char* USAGE_SET_DESCRIPTION(
"set_description [free text note]");
212 const char* USAGE_SIGN(
"sign <filename>");
213 const char* USAGE_VERIFY(
"verify <filename> <address> <signature>");
214 const char* USAGE_EXPORT_KEY_IMAGES(
"export_key_images <filename>");
215 const char* USAGE_IMPORT_KEY_IMAGES(
"import_key_images <filename>");
216 const char* USAGE_HW_KEY_IMAGES_SYNC(
"hw_key_images_sync");
217 const char* USAGE_HW_RECONNECT(
"hw_reconnect");
218 const char* USAGE_EXPORT_OUTPUTS(
"export_outputs <filename>");
219 const char* USAGE_IMPORT_OUTPUTS(
"import_outputs <filename>");
220 const char* USAGE_SHOW_TRANSFER(
"show_transfer <txid>");
221 const char* USAGE_MAKE_MULTISIG(
"make_multisig <threshold> <string1> [<string>...]");
222 const char* USAGE_FINALIZE_MULTISIG(
"finalize_multisig <string> [<string>...]");
223 const char* USAGE_EXCHANGE_MULTISIG_KEYS(
"exchange_multisig_keys <string> [<string>...]");
224 const char* USAGE_EXPORT_MULTISIG_INFO(
"export_multisig_info <filename>");
225 const char* USAGE_IMPORT_MULTISIG_INFO(
"import_multisig_info <filename> [<filename>...]");
226 const char* USAGE_SIGN_MULTISIG(
"sign_multisig <filename>");
227 const char* USAGE_SUBMIT_MULTISIG(
"submit_multisig <filename>");
228 const char* USAGE_EXPORT_RAW_MULTISIG_TX(
"export_raw_multisig_tx <filename>");
229 const char* USAGE_MMS(
"mms [<subcommand> [<subcommand_parameters>]]");
230 const char* USAGE_MMS_INIT(
"mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
231 const char* USAGE_MMS_INFO(
"mms info");
232 const char* USAGE_MMS_SIGNER(
"mms signer [<number> <label> [<transport_address> [<etn_address>]]]");
233 const char* USAGE_MMS_LIST(
"mms list");
234 const char* USAGE_MMS_NEXT(
"mms next [sync]");
235 const char* USAGE_MMS_SYNC(
"mms sync");
236 const char* USAGE_MMS_TRANSFER(
"mms transfer <transfer_command_arguments>");
237 const char* USAGE_MMS_DELETE(
"mms delete (<message_id> | all)");
238 const char* USAGE_MMS_SEND(
"mms send [<message_id>]");
239 const char* USAGE_MMS_RECEIVE(
"mms receive");
240 const char* USAGE_MMS_EXPORT(
"mms export <message_id>");
241 const char* USAGE_MMS_NOTE(
"mms note [<label> <text>]");
242 const char* USAGE_MMS_SHOW(
"mms show <message_id>");
243 const char* USAGE_MMS_SET(
"mms set <option_name> [<option_value>]");
244 const char* USAGE_MMS_SEND_SIGNER_CONFIG(
"mms send_signer_config");
245 const char* USAGE_MMS_START_AUTO_CONFIG(
"mms start_auto_config [<label> <label> ...]");
246 const char* USAGE_MMS_STOP_AUTO_CONFIG(
"mms stop_auto_config");
247 const char* USAGE_MMS_AUTO_CONFIG(
"mms auto_config <auto_config_token>");
248 const char* USAGE_PRINT_RING(
"print_ring <key_image> | <txid>");
249 const char* USAGE_SET_RING(
"set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )");
250 const char* USAGE_UNSET_RING(
"unset_ring <txid> | ( <key_image> [<key_image>...] )");
251 const char* USAGE_SAVE_KNOWN_RINGS(
"save_known_rings");
252 const char* USAGE_MARK_OUTPUT_SPENT(
"mark_output_spent <amount>/<offset> | <filename> [add]");
253 const char* USAGE_MARK_OUTPUT_UNSPENT(
"mark_output_unspent <amount>/<offset>");
254 const char* USAGE_IS_OUTPUT_SPENT(
"is_output_spent <amount>/<offset>");
255 const char* USAGE_FREEZE(
"freeze <key_image>");
256 const char* USAGE_THAW(
"thaw <key_image>");
257 const char* USAGE_FROZEN(
"frozen <key_image>");
258 const char* USAGE_NET_STATS(
"net_stats");
259 const char* USAGE_WELCOME(
"welcome");
260 const char* USAGE_VERSION(
"version");
261 const char* USAGE_HELP(
"help [<command>]");
270 std::cout <<
" (Y/Yes/N/No)";
271 std::cout <<
": " << std::flush;
275 buf = tools::input_line_win();
277 std::getline(std::cin,
buf);
291 MERROR(
"Failed to read secure line");
301 boost::optional<tools::password_container> password_prompter(
const char *prompt,
bool verify)
311 return pwd_container;
314 boost::optional<tools::password_container> default_password_prompter(
bool verify)
316 return password_prompter(verify ?
sw::tr(
"Enter a new password for the wallet") :
sw::tr(
"Wallet password"), verify);
326 err =
sw::tr(
"daemon is busy. Please try again later.");
335 err =
sw::tr(
"possibly lost connection to daemon");
355 bool parse_bool(
const std::string& s,
bool& result)
368 boost::algorithm::is_iequal ignore_case{};
369 if (boost::algorithm::equals(
"true", s, ignore_case) || boost::algorithm::equals(
simple_wallet::tr(
"true"), s, ignore_case))
374 if (boost::algorithm::equals(
"false", s, ignore_case) || boost::algorithm::equals(
simple_wallet::tr(
"false"), s, ignore_case))
383 template <
typename F>
387 if (parse_bool(s, r))
403 } refresh_type_names[] =
414 for (
size_t n = 0; n <
sizeof(refresh_type_names) /
sizeof(refresh_type_names[0]); ++n)
416 if (s == refresh_type_names[n].
name)
418 refresh_type = refresh_type_names[n].refresh_type;
428 for (
size_t n = 0; n <
sizeof(refresh_type_names) /
sizeof(refresh_type_names[0]); ++n)
430 if (type == refresh_type_names[n].refresh_type)
431 return refresh_type_names[n].name;
441 std::string oa_prompter(
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)
443 if (addresses.empty())
450 dnssec_str =
sw::tr(
"DNSSEC validation passed");
454 dnssec_str =
sw::tr(
"WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
456 std::stringstream prompt;
457 prompt <<
sw::tr(
"For URL: ") << url
458 <<
", " << dnssec_str << std::endl
459 <<
sw::tr(
" Electroneum Address = ") << addresses[0]
464 std::string confirm_dns_ok = input_line(prompt.str(),
true);
471 std::cout <<
sw::tr(
"you have cancelled the transfer request") << std::endl;
477 bool parse_subaddress_indices(
const std::string& arg, std::set<uint32_t>& subaddr_indices)
479 subaddr_indices.clear();
481 if (arg.substr(0, 6) !=
"index=")
483 std::string subaddr_indices_str_unsplit = arg.substr(6, arg.size() - 6);
484 std::vector<std::string> subaddr_indices_str;
485 boost::split(subaddr_indices_str, subaddr_indices_str_unsplit, boost::is_any_of(
","));
487 for (
const auto& subaddr_index_str : subaddr_indices_str)
493 subaddr_indices.clear();
496 subaddr_indices.insert(subaddr_index);
509 void handle_transfer_exception(
const std::exception_ptr &e,
bool trusted_daemon)
511 bool warn_of_possible_attack = !trusted_daemon;
514 std::rethrow_exception(e);
535 LOG_PRINT_L0(boost::format(
"not enough ETN to transfer, available only %s, sent amount %s") %
539 warn_of_possible_attack =
false;
543 LOG_PRINT_L0(boost::format(
"not enough ETN to transfer, available only %s, sent amount %s") %
547 warn_of_possible_attack =
false;
551 LOG_PRINT_L0(boost::format(
"not enough ETN to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
556 fail_msg_writer() <<
sw::tr(
"Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more ETN than the unlocked balance, or not leaving enough for fees");
557 warn_of_possible_attack =
false;
562 writer <<
sw::tr(
"not enough outputs for specified ring size") <<
" = " << (e.
mixin_count() + 1) <<
":";
563 for (std::pair<uint64_t, uint64_t> outs_for_amount : e.
scanty_outs())
565 writer <<
"\n" <<
sw::tr(
"output amount") <<
" = " <<
print_etn(outs_for_amount.first) <<
", " <<
sw::tr(
"found outputs to use") <<
" = " << outs_for_amount.second;
567 writer <<
sw::tr(
"Please use sweep_unmixable.");
572 warn_of_possible_attack =
false;
584 warn_of_possible_attack =
false;
589 warn_of_possible_attack =
false;
594 warn_of_possible_attack =
false;
605 warn_of_possible_attack =
false;
612 catch (
const std::exception& e)
614 LOG_ERROR(
"unexpected error: " << e.what());
618 if (warn_of_possible_attack)
619 fail_msg_writer() <<
sw::tr(
"There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
622 bool check_file_overwrite(
const std::string &filename)
624 boost::system::error_code errcode;
625 if (boost::filesystem::exists(filename, errcode))
627 if (boost::ends_with(filename,
".keys"))
629 fail_msg_writer() << boost::format(
sw::tr(
"File %s likely stores wallet private keys! Use a different file name.")) % filename;
632 return command_line::is_yes(input_line((boost::format(
sw::tr(
"File %s already exists. Are you sure to overwrite it?")) % filename).str(),
true));
639 static constexpr
const char hex[] =
u8"0123456789abcdef";
641 for (
size_t i = 0, sz =
sizeof(k); i < sz; ++i)
643 putchar(hex[*ptr >> 4]);
644 putchar(hex[*ptr & 15]);
652 auto priority_pos = std::find(
653 allowed_priority_strings.begin(),
654 allowed_priority_strings.end(),
656 if(priority_pos != allowed_priority_strings.end()) {
657 priority = std::distance(allowed_priority_strings.begin(), priority_pos);
666 for (
size_t n = 0; n < allowed_priority_strings.size(); ++n)
670 s += allowed_priority_strings[n];
677 std::stringstream ss;
678 ss <<
tr(
"Commands: ") <<
ENDL;
680 boost::replace_all(usage,
"\n",
"\n ");
681 usage.insert(0,
" ");
686 std::string simple_wallet::get_command_usage(
const std::vector<std::string> &args)
688 std::pair<std::string, std::string> documentation = m_cmd_binder.get_documentation(args);
689 std::stringstream ss;
690 if(documentation.first.empty())
692 ss <<
tr(
"Unknown command: ") << args.front();
696 std::string usage = documentation.second.empty() ? args.front() : documentation.first;
697 std::string description = documentation.second.empty() ? documentation.first : documentation.second;
698 usage.insert(0,
" ");
700 boost::replace_all(description,
"\n",
"\n ");
701 description.insert(0,
" ");
702 ss <<
tr(
"Command description: ") <<
ENDL << description <<
ENDL;
707 bool simple_wallet::viewkey(
const std::vector<std::string> &args)
711 if (m_wallet->key_on_device()) {
712 std::cout <<
"secret: On device. Not available" << std::endl;
716 print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
719 std::cout <<
"public: " <<
string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
726 if (m_wallet->watch_only())
733 if (m_wallet->key_on_device()) {
734 std::cout <<
"secret: On device. Not available" << std::endl;
738 print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
742 unsigned char seckey1[32];
743 unsigned char public_key64[65];
747 memcpy(seckey1, m_wallet->get_account().get_keys().m_spend_secret_key.data, 32);
755 LOG_ERROR(
"Failed to create secp256k1 public key");
766 keccak(public_key64 + 1, 64, pubkey1.
data, 32);
770 std::string bridge_smartchain_address =
"0x" + hex_address;
771 LOG_PRINT_L1(
"Smartchain address: " << bridge_smartchain_address);
773 std::cout <<
"smartchain address: " << bridge_smartchain_address << std::endl;
775 std::cout <<
"public: " <<
string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
780 bool simple_wallet::print_seed(
bool encrypted)
784 bool ready, multisig;
786 if (m_wallet->key_on_device())
791 if (m_wallet->watch_only())
797 multisig = m_wallet->multisig(&ready);
809 if (!multisig && !m_wallet->is_deterministic())
818 auto pwd_container = password_prompter(
tr(
"Enter optional seed offset passphrase, empty to see raw seed"),
true);
819 if (std::cin.eof() || !pwd_container)
821 seed_pass = pwd_container->password();
825 success = m_wallet->get_multisig_seed(seed, seed_pass);
826 else if (m_wallet->is_deterministic())
827 success = m_wallet->get_seed(seed, seed_pass);
840 bool simple_wallet::seed(
const std::vector<std::string> &args)
842 return print_seed(
false);
845 bool simple_wallet::encrypted_seed(
const std::vector<std::string> &args)
847 return print_seed(
true);
850 bool simple_wallet::seed_set_language(
const std::vector<std::string> &args)
852 if (m_wallet->key_on_device())
857 if (m_wallet->multisig())
862 if (m_wallet->watch_only())
872 if (!m_wallet->is_deterministic())
881 pwd_container = get_and_verify_password();
882 if (pwd_container == boost::none)
888 password = pwd_container->password();
891 std::string mnemonic_language = get_mnemonic_language();
892 if (mnemonic_language.empty())
895 m_wallet->set_seed_language(
std::move(mnemonic_language));
896 m_wallet->rewrite(m_wallet_file, password);
900 bool simple_wallet::change_password(
const std::vector<std::string> &args)
902 const auto orig_pwd_container = get_and_verify_password();
904 if(orig_pwd_container == boost::none)
911 const auto pwd_container = default_password_prompter(
true);
917 m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
928 bool simple_wallet::payment_id(
const std::vector<std::string> &args)
938 payment_id = crypto::rand<crypto::hash>();
943 bool simple_wallet::print_fee_info(
const std::vector<std::string> &args)
945 if (!try_connect_to_daemon())
948 const uint64_t base_fee = m_wallet->get_base_fee();
949 const char *base = per_byte ?
"byte" :
"kB";
953 for (
uint32_t priority = 1; priority <= 4; ++priority)
955 uint64_t mult = m_wallet->get_fee_multiplier(priority);
956 uint64_t fee = base_fee * (per_byte ? 2500 : 13) * mult;
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))
1681 rings.push_back({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(),
'-'))
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](
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](
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)
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;
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") %
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.");
5555 catch (
const std::exception& e)
5557 LOG_ERROR(
"unexpected error: " << e.what());
5569 std::pair<std::string, std::string> simple_wallet::show_outputs_line(
const std::vector<uint64_t> &heights,
uint64_t blockchain_height,
uint64_t highlight_height)
const 5571 std::stringstream ostr;
5574 blockchain_height = std::max(blockchain_height, h);
5576 for (
size_t j = 0; j < heights.size(); ++j)
5577 ostr << (heights[j] == highlight_height ?
" *" :
" ") << heights[j];
5582 for (
size_t j = 0; j < heights.size(); ++j)
5584 uint64_t pos = (heights[j] * resolution) / blockchain_height;
5585 ring_str[pos] =
'o';
5587 if (highlight_height < blockchain_height)
5589 uint64_t pos = (highlight_height * resolution) / blockchain_height;
5590 ring_str[pos] =
'*';
5593 return std::make_pair(ostr.str(), ring_str);
5596 bool simple_wallet::print_ring_members(
const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
5599 if (!try_connect_to_daemon(
false, &
version))
5605 uint64_t blockchain_height = get_daemon_blockchain_height(err);
5612 for (
size_t n = 0; n < ptx_vector.size(); ++n)
5618 std::vector<uint64_t> spent_key_height(tx.
vin.size());
5619 std::vector<crypto::hash> spent_key_txid (tx.
vin.size());
5620 for (
size_t i = 0; i < tx.
vin.size(); ++i)
5627 for (
const auto &src: construction_data.
sources)
5628 if (src.outputs[src.real_output].second.dest == td.get_public_key())
5642 req.outputs.resize(absolute_offsets.size());
5643 for (
size_t j = 0; j < absolute_offsets.size(); ++j)
5645 req.outputs[j].amount = in_key.
amount;
5646 req.outputs[j].index = absolute_offsets[j];
5649 bool r = m_wallet->invoke_http_bin(
"/get_outs.bin", req,
res);
5650 err = interpret_rpc_response(r,
res.status);
5657 for (
auto& res_out :
res.outs)
5659 if (res_out.height >= blockchain_height)
5661 fail_msg_writer() <<
tr(
"output key's originating block height shouldn't be higher than the blockchain height");
5665 ostr <<
tr(
"\nOriginating block heights: ");
5666 spent_key_height[i] =
res.outs[
source.real_output].height;
5667 spent_key_txid [i] =
res.outs[
source.real_output].txid;
5668 std::vector<uint64_t> heights(absolute_offsets.size(), 0);
5669 uint64_t highlight_height = std::numeric_limits<uint64_t>::max();
5670 for (
size_t j = 0; j < absolute_offsets.size(); ++j)
5672 heights[j] =
res.outs[j].height;
5673 if (j ==
source.real_output)
5674 highlight_height = heights[j];
5676 std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
5677 ostr << ring_str.first <<
tr(
"\n|") << ring_str.second <<
tr(
"|\n");
5680 bool are_keys_from_same_tx =
false;
5681 bool are_keys_from_close_height =
false;
5682 for (
size_t i = 0; i < tx.
vin.size(); ++i) {
5683 for (
size_t j = i + 1; j < tx.
vin.size(); ++j)
5685 if (spent_key_txid[i] == spent_key_txid[j])
5686 are_keys_from_same_tx =
true;
5687 if (std::abs((
int64_t)(spent_key_height[i] - spent_key_height[j])) < (
int64_t)5)
5688 are_keys_from_close_height =
true;
5691 if (are_keys_from_same_tx || are_keys_from_close_height)
5694 <<
tr(
"\nWarning: Some input keys being spent are from ")
5695 << (are_keys_from_same_tx ?
tr(
"the same transaction") :
tr(
"blocks that are temporally very close"))
5696 <<
tr(
", which can break the anonymity of ring signature. Make sure this is intentional!");
5703 bool simple_wallet::transfer_main(
int transfer_type,
const std::vector<std::string> &args_,
bool called_by_mms)
5706 if (!try_connect_to_daemon())
5709 std::vector<std::string> local_args = args_;
5711 std::set<uint32_t> subaddr_indices;
5712 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
5714 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
5716 local_args.erase(local_args.begin());
5720 if (local_args.size() > 0 &&
parse_priority(local_args[0], priority))
5721 local_args.erase(local_args.begin());
5723 priority = m_wallet->adjust_priority(priority);
5725 size_t fake_outs_count = 0;
5726 if(local_args.size() > 0) {
5730 fake_outs_count = m_wallet->default_mixin();
5731 if (fake_outs_count == 0)
5734 else if (ring_size == 0)
5741 fake_outs_count = ring_size - 1;
5742 local_args.erase(local_args.begin());
5746 uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
5747 if (adjusted_fake_outs_count > fake_outs_count)
5749 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
5752 if (adjusted_fake_outs_count < fake_outs_count)
5754 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
5758 const size_t min_args = (transfer_type ==
TransferLocked) ? 2 : 1;
5759 if(local_args.size() < min_args)
5765 std::vector<uint8_t> extra;
5766 bool payment_id_seen =
false;
5767 if (!local_args.empty())
5779 local_args.pop_back();
5780 payment_id_seen =
true;
5781 message_writer() <<
tr(
"Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead");
5795 locked_blocks = boost::lexical_cast<
uint64_t>(local_args.back());
5797 catch (
const std::exception &e)
5799 fail_msg_writer() <<
tr(
"bad locked_blocks parameter:") <<
" " << local_args.back();
5802 if (locked_blocks > 1000000)
5807 local_args.pop_back();
5810 vector<cryptonote::address_parse_info> dsts_info;
5811 vector<cryptonote::tx_destination_entry> dsts;
5812 size_t num_subaddresses = 0;
5813 for (
size_t i = 0; i < local_args.size(); )
5815 dsts_info.emplace_back();
5821 std::string address_uri, payment_id_uri, tx_description, recipient_name,
error;
5822 std::vector<std::string> unknown_parameters;
5824 bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters,
error);
5828 if (payment_id_uri.size() == 16)
5835 info.has_payment_id =
true;
5841 else if (i + 1 < local_args.size())
5845 if(!ok || 0 == de.
amount)
5847 fail_msg_writer() <<
tr(
"amount is wrong: ") << local_args[i] <<
' ' << local_args[i + 1] <<
5848 ", " <<
tr(
"expected number from 0 to ") <<
print_etn(std::numeric_limits<uint64_t>::max());
5856 if (boost::starts_with(local_args[i],
"electroneum:"))
5871 num_subaddresses +=
info.is_subaddress;
5873 if (
info.has_payment_id || !payment_id_uri.empty())
5875 if (payment_id_seen)
5877 fail_msg_writer() <<
tr(
"a single transaction cannot use more than one payment id");
5883 if (
info.has_payment_id)
5885 memcpy(payment_id.data,
info.payment_id.data, 8);
5886 memset(payment_id.data + 8, 0, 24);
5902 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
5905 payment_id_seen =
true;
5912 if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
5914 std::string accepted = input_line(
tr(
"No payment id is included with this transaction. Is this okay?"),
true);
5930 std::vector<tools::wallet2::pending_tx> ptx_vector;
5931 uint64_t bc_height, unlock_block = 0;
5933 switch (transfer_type)
5936 bc_height = get_daemon_blockchain_height(err);
5942 unlock_block = bc_height + locked_blocks;
5943 ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block , priority, extra, m_current_subaddress_account, subaddr_indices);
5946 LOG_ERROR(
"Unknown transfer method, using default");
5949 ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 , priority, extra, m_current_subaddress_account, subaddr_indices);
5953 if (ptx_vector.empty())
5960 if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
5967 for (
size_t n = 0; n < ptx_vector.size(); ++n)
5969 total_fee += ptx_vector[n].fee;
5970 for (
auto i: ptx_vector[n].selected_transfers)
5971 total_sent += m_wallet->get_transfer_details(i).amount();
5972 total_sent -= ptx_vector[n].change_dts.amount + ptx_vector[n].fee;
5973 change += ptx_vector[n].change_dts.amount;
5975 if (ptx_vector[n].dust_added_to_fee)
5976 dust_in_fee += ptx_vector[n].dust;
5978 dust_not_in_fee += ptx_vector[n].dust;
5981 std::stringstream prompt;
5982 for (
size_t n = 0; n < ptx_vector.size(); ++n)
5984 prompt <<
tr(
"\nTransaction ") << (n + 1) <<
"/" << ptx_vector.size() <<
":\n";
5985 subaddr_indices.clear();
5987 subaddr_indices.insert(i);
5989 prompt << boost::format(
tr(
"Spending from address index %d\n")) % i;
5990 if (subaddr_indices.size() > 1)
5991 prompt <<
tr(
"WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
5993 prompt << boost::format(
tr(
"Sending %s. ")) %
print_etn(total_sent);
5994 if (ptx_vector.size() > 1)
5996 prompt << boost::format(
tr(
"Your transaction needs to be split into %llu transactions. " 5997 "This will result in a transaction fee being applied to each transaction, for a total fee of %s")) %
5998 ((
unsigned long long)ptx_vector.size()) %
print_etn(total_fee);
6002 prompt << boost::format(
tr(
"The transaction fee is %s")) %
6005 if (dust_in_fee != 0) prompt << boost::format(
tr(
", of which %s is dust from change")) %
print_etn(dust_in_fee);
6006 if (dust_not_in_fee != 0) prompt <<
tr(
".") <<
ENDL << boost::format(
tr(
"A total of %s from dust change will be sent to dust address"))
6010 float days = locked_blocks / 720.0f;
6011 prompt << boost::format(
tr(
".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) %
cryptonote::print_etn(change) % ((
unsigned long long)unlock_block) % days;
6013 if (m_wallet->print_ring_members())
6015 if (!print_ring_members(ptx_vector, prompt))
6018 bool default_ring_size =
true;
6019 for (
const auto &ptx: ptx_vector)
6021 for (
const auto &vin: ptx.tx.vin)
6025 const txin_to_key& in_to_key = boost::get<txin_to_key>(vin);
6027 default_ring_size =
false;
6031 if (m_wallet->confirm_non_default_ring_size() && !default_ring_size)
6033 prompt <<
tr(
"WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
6035 prompt <<
ENDL <<
tr(
"Is this okay?");
6037 std::string accepted = input_line(prompt.str(),
true);
6050 if (m_wallet->multisig() && called_by_mms)
6052 std::string ciphertext = m_wallet->save_multisig_tx(ptx_vector);
6053 if (!ciphertext.empty())
6059 else if (m_wallet->multisig())
6061 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6069 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6072 else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6082 commit_or_save(signed_tx.
ptx, m_do_not_relay);
6084 catch (
const std::exception& e)
6086 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6096 else if (m_wallet->watch_only())
6098 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6106 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6111 commit_or_save(ptx_vector, m_do_not_relay);
6114 catch (
const std::exception &e)
6116 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6129 bool simple_wallet::transfer(
const std::vector<std::string> &args_)
6131 transfer_main(
Transfer, args_,
false);
6135 bool simple_wallet::locked_transfer(
const std::vector<std::string> &args_)
6141 bool simple_wallet::locked_sweep_all(
const std::vector<std::string> &args_)
6143 return sweep_main(0,
true, args_);
6147 bool simple_wallet::sweep_unmixable(
const std::vector<std::string> &args_)
6149 if (!try_connect_to_daemon())
6157 auto ptx_vector = m_wallet->create_unmixable_sweep_transactions();
6159 if (ptx_vector.empty())
6166 uint64_t total_fee = 0, total_unmixable = 0;
6167 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6169 total_fee += ptx_vector[n].fee;
6170 for (
auto i: ptx_vector[n].selected_transfers)
6171 total_unmixable += m_wallet->get_transfer_details(i).amount();
6175 if (ptx_vector.size() > 1) {
6176 prompt_str = (boost::format(
tr(
"Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6178 ((
unsigned long long)ptx_vector.size()) %
6182 prompt_str = (boost::format(
tr(
"Sweeping %s for a total fee of %s. Is this okay?")) %
6186 std::string accepted = input_line(prompt_str,
true);
6197 if (m_wallet->multisig())
6199 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6206 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6209 else if (m_wallet->watch_only())
6211 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6218 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6223 commit_or_save(ptx_vector, m_do_not_relay);
6229 std::string accepted = input_line((boost::format(
tr(
"Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay?")) %
print_etn(e.
available())).str(),
true);
6236 m_wallet->discard_unmixable_outputs();
6240 catch (
const std::exception &e)
6242 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6253 bool simple_wallet::sweep_main(
uint64_t below,
bool locked,
const std::vector<std::string> &args_)
6255 auto print_usage = [below]()
6266 if (args_.size() == 0)
6273 if (!try_connect_to_daemon())
6276 std::vector<std::string> local_args = args_;
6278 std::set<uint32_t> subaddr_indices;
6279 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
6281 if (local_args[0] ==
"index=all")
6283 for (
uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i)
6284 subaddr_indices.insert(i);
6286 else if (!parse_subaddress_indices(local_args[0], subaddr_indices))
6291 local_args.erase(local_args.begin());
6295 if (local_args.size() > 0 &&
parse_priority(local_args[0], priority))
6296 local_args.erase(local_args.begin());
6298 priority = m_wallet->adjust_priority(priority);
6300 size_t fake_outs_count = 0;
6301 if(local_args.size() > 0) {
6305 fake_outs_count = m_wallet->default_mixin();
6306 if (fake_outs_count == 0)
6309 else if (ring_size == 0)
6316 fake_outs_count = ring_size - 1;
6317 local_args.erase(local_args.begin());
6321 uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6322 if (adjusted_fake_outs_count > fake_outs_count)
6324 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6327 if (adjusted_fake_outs_count < fake_outs_count)
6329 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6337 if (local_args.size() < 2) {
6344 locked_blocks = boost::lexical_cast<
uint64_t>(local_args[1]);
6346 catch (
const std::exception &e)
6351 if (locked_blocks > 1000000)
6357 uint64_t bc_height = get_daemon_blockchain_height(err);
6363 unlock_block = bc_height + locked_blocks;
6365 local_args.erase(local_args.begin() + 1);
6369 if (local_args.size() > 0 && local_args[0].substr(0, 8) ==
"outputs=")
6376 else if (outputs < 1)
6383 local_args.erase(local_args.begin());
6387 std::vector<uint8_t> extra;
6388 bool payment_id_seen =
false;
6389 if (local_args.size() >= 2)
6402 payment_id_seen =
true;
6405 if(!r && local_args.size() == 3)
6407 fail_msg_writer() <<
tr(
"payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str;
6411 if (payment_id_seen)
6412 local_args.pop_back();
6423 if (
info.has_payment_id)
6425 if (payment_id_seen)
6427 fail_msg_writer() <<
tr(
"a single transaction cannot use more than one payment id: ") << local_args[0];
6433 memcpy(payment_id.data,
info.payment_id.data, 8);
6434 memset(payment_id.data + 8, 0, 24);
6440 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
6443 payment_id_seen =
true;
6447 if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !
info.is_subaddress)
6449 std::string accepted = input_line(
tr(
"No payment id is included with this transaction. Is this okay?"),
true);
6465 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);
6467 if (ptx_vector.empty())
6474 uint64_t total_fee = 0, total_sent = 0;
6475 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6477 total_fee += ptx_vector[n].fee;
6478 for (
auto i: ptx_vector[n].selected_transfers)
6479 total_sent += m_wallet->get_transfer_details(i).amount();
6482 std::ostringstream prompt;
6483 for (
size_t n = 0; n < ptx_vector.size(); ++n)
6485 prompt <<
tr(
"\nTransaction ") << (n + 1) <<
"/" << ptx_vector.size() <<
":\n";
6486 subaddr_indices.clear();
6488 subaddr_indices.insert(i);
6490 prompt << boost::format(
tr(
"Spending from address index %d\n")) % i;
6491 if (subaddr_indices.size() > 1)
6492 prompt <<
tr(
"WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
6494 if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
6496 if (ptx_vector.size() > 1) {
6497 prompt << boost::format(
tr(
"Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
6499 ((
unsigned long long)ptx_vector.size()) %
6503 prompt << boost::format(
tr(
"Sweeping %s for a total fee of %s. Is this okay?")) %
6507 std::string accepted = input_line(prompt.str(),
true);
6518 if (m_wallet->multisig())
6520 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6527 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6530 else if (m_wallet->get_account().get_device().has_tx_cold_sign())
6535 std::vector<cryptonote::address_parse_info> dsts_info;
6536 dsts_info.push_back(
info);
6543 commit_or_save(signed_tx.
ptx, m_do_not_relay);
6545 catch (
const std::exception& e)
6547 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6555 else if (m_wallet->watch_only())
6557 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6564 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6569 commit_or_save(ptx_vector, m_do_not_relay);
6572 catch (
const std::exception& e)
6574 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6585 bool simple_wallet::sweep_single(
const std::vector<std::string> &args_)
6587 if (!try_connect_to_daemon())
6590 std::vector<std::string> local_args = args_;
6593 if (local_args.size() > 0 &&
parse_priority(local_args[0], priority))
6594 local_args.erase(local_args.begin());
6596 priority = m_wallet->adjust_priority(priority);
6598 size_t fake_outs_count = 0;
6599 if(local_args.size() > 0) {
6603 fake_outs_count = m_wallet->default_mixin();
6604 if (fake_outs_count == 0)
6607 else if (ring_size == 0)
6614 fake_outs_count = ring_size - 1;
6615 local_args.erase(local_args.begin());
6619 uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
6620 if (adjusted_fake_outs_count > fake_outs_count)
6622 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6625 if (adjusted_fake_outs_count < fake_outs_count)
6627 fail_msg_writer() << (boost::format(
tr(
"ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
6632 if (local_args.size() > 0 && local_args[0].substr(0, 8) ==
"outputs=")
6639 else if (outputs < 1)
6646 local_args.erase(local_args.begin());
6650 std::vector<uint8_t> extra;
6651 bool payment_id_seen =
false;
6652 if (local_args.size() == 3)
6670 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
6674 local_args.pop_back();
6675 payment_id_seen =
true;
6678 if (local_args.size() != 2)
6698 if (
info.has_payment_id)
6700 if (payment_id_seen)
6702 fail_msg_writer() <<
tr(
"a single transaction cannot use more than one payment id: ") << local_args[0];
6708 memcpy(payment_id.data,
info.payment_id.data, 8);
6709 memset(payment_id.data + 8, 0, 24);
6714 fail_msg_writer() <<
tr(
"failed to set up payment id, though it was decoded correctly");
6717 payment_id_seen =
true;
6721 if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !
info.is_subaddress)
6723 std::string accepted = input_line(
tr(
"No payment id is included with this transaction. Is this okay?"),
true);
6741 auto ptx_vector = m_wallet->create_transactions_single(ki,
info.address,
info.is_subaddress, outputs, fake_outs_count, 0 , priority, extra);
6743 if (ptx_vector.empty())
6748 if (ptx_vector.size() > 1)
6750 fail_msg_writer() <<
tr(
"Multiple transactions are created, which is not supposed to happen");
6753 if (ptx_vector[0].selected_transfers.size() != 1)
6755 fail_msg_writer() <<
tr(
"The transaction uses multiple or no inputs, which is not supposed to happen");
6760 uint64_t total_fee = ptx_vector[0].fee;
6761 uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount();
6762 std::ostringstream prompt;
6763 if (!print_ring_members(ptx_vector, prompt))
6765 prompt << boost::format(
tr(
"Sweeping %s for a total fee of %s. Is this okay?")) %
6768 std::string accepted = input_line(prompt.str(),
true);
6778 if (m_wallet->multisig())
6780 bool r = m_wallet->save_multisig_tx(ptx_vector,
"multisig_electroneum_tx");
6787 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"multisig_electroneum_tx";
6790 else if (m_wallet->watch_only())
6792 bool r = m_wallet->save_tx(ptx_vector,
"unsigned_electroneum_tx");
6799 success_msg_writer(
true) <<
tr(
"Unsigned transaction(s) successfully written to file: ") <<
"unsigned_electroneum_tx";
6804 m_wallet->commit_tx(ptx_vector[0]);
6809 catch (
const std::exception& e)
6811 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
6822 bool simple_wallet::sweep_all(
const std::vector<std::string> &args_)
6824 return sweep_main(0,
false, args_);
6827 bool simple_wallet::sweep_below(
const std::vector<std::string> &args_)
6830 if (args_.size() < 1)
6840 return sweep_main(below,
false, std::vector<std::string>(++args_.begin(), args_.end()));
6843 bool simple_wallet::donate(
const std::vector<std::string> &args_)
6845 std::vector<std::string> local_args = args_;
6846 if(local_args.empty() || local_args.size() > 5)
6859 payment_id_str = local_args.back();
6860 local_args.pop_back();
6865 if (ok && amount != 0)
6867 amount_str = local_args.back();
6868 local_args.pop_back();
6872 fail_msg_writer() <<
tr(
"amount is wrong: ") << local_args.back() <<
", " <<
tr(
"expected number from 0 to ") <<
print_etn(std::numeric_limits<uint64_t>::max());
6892 local_args.push_back(address_str);
6893 local_args.push_back(amount_str);
6894 if (!payment_id_str.empty())
6895 local_args.push_back(payment_id_str);
6900 transfer(local_args);
6907 uint64_t amount = 0, amount_to_dests = 0, change = 0;
6908 size_t min_ring_size = ~0;
6909 std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
6910 int first_known_non_zero_change_index = -1;
6912 for (
size_t n = 0; n < get_num_txes(); ++n)
6916 std::vector<tx_extra_field> tx_extra_fields;
6917 bool has_encrypted_payment_id =
false;
6927 if (!payment_id_string.empty())
6928 payment_id_string +=
", ";
6931 bool is_dummy =
true;
6932 for (
const auto &e: cd.
dests)
6933 if (e.is_integrated)
6938 payment_id_string +=
std::string(
"dummy payment ID");
6943 memcpy(payment_id.data, payment_id8.data, 8);
6944 memset(payment_id.data + 8, 0, 24);
6950 if (!payment_id_string.empty())
6951 payment_id_string +=
", ";
6953 payment_id_string +=
" (OBSOLETE)";
6958 for (
size_t s = 0; s < cd.
sources.size(); ++s)
6960 amount += cd.
sources[s].amount;
6961 size_t ring_size = cd.
sources[s].outputs.size();
6962 if (ring_size < min_ring_size)
6963 min_ring_size = ring_size;
6976 auto i = dests.find(entry.
addr);
6977 if (i == dests.end())
6978 dests.insert(std::make_pair(entry.
addr, std::make_pair(
address, entry.
amount)));
6980 i->second.second += entry.
amount;
6981 amount_to_dests += entry.
amount;
6986 if (it == dests.end())
6993 fail_msg_writer() <<
tr(
"Claimed change is larger than payment to the change address");
6998 if (first_known_non_zero_change_index == -1)
6999 first_known_non_zero_change_index = n;
7008 if (it->second.second == 0)
7013 if (payment_id_string.empty())
7014 payment_id_string =
"no payment ID";
7017 size_t n_dummy_outputs = 0;
7018 for (
auto i = dests.begin(); i != dests.end(); )
7020 if (i->second.second > 0)
7022 if (!dest_string.empty())
7023 dest_string +=
", ";
7024 dest_string += (boost::format(
tr(
"sending %s to %s")) %
print_etn(i->second.second) % i->second.first).str();
7030 if (n_dummy_outputs > 0)
7032 if (!dest_string.empty())
7033 dest_string +=
", ";
7036 if (dest_string.empty())
7037 dest_string =
tr(
"with no destinations");
7043 change_string += (boost::format(
tr(
"%s change to %s")) %
print_etn(change) %
address).str();
7046 change_string +=
tr(
"no change");
7048 uint64_t fee = amount - amount_to_dests;
7049 std::string prompt_str = (boost::format(
tr(
"Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay?")) % (
unsigned long)get_num_txes() %
print_etn(amount) %
print_etn(fee) % dest_string % change_string % (
unsigned long)min_ring_size % payment_id_string % extra_message).str();
7057 extra_message = (boost::format(
"%u outputs to import. ") % (unsigned)txs.
transfers.second.size()).str();
7065 extra_message = (boost::format(
"%u key images to import. ") % (unsigned)txs.
key_images.size()).str();
7069 bool simple_wallet::sign_transfer(
const std::vector<std::string> &args_)
7071 if (m_wallet->key_on_device())
7076 if(m_wallet->multisig())
7078 fail_msg_writer() <<
tr(
"This is a multisig wallet, it can only sign with sign_multisig");
7081 if(m_wallet->watch_only())
7086 if (args_.size() > 1 || (args_.size() == 1 && args_[0] !=
"export_raw"))
7093 const bool export_raw = args_.size() == 1;
7095 std::vector<tools::wallet2::pending_tx> ptx;
7098 bool r = m_wallet->sign_tx(
"unsigned_electroneum_tx",
"signed_electroneum_tx", ptx, [&](
const tools::wallet2::unsigned_tx_set &tx){
return accept_loaded_tx(tx); }, export_raw);
7105 catch (
const std::exception &e)
7112 for (
const auto &t: ptx)
7114 if (!txids_as_text.empty())
7115 txids_as_text += (
", ");
7118 success_msg_writer(
true) <<
tr(
"Transaction successfully signed to file ") <<
"signed_electroneum_tx" <<
", txid " << txids_as_text;
7122 for (
size_t i = 0; i < ptx.size(); ++i)
7125 rawfiles_as_text +=
", ";
7126 rawfiles_as_text +=
"signed_electroneum_tx_raw" + (ptx.size() == 1 ?
"" : (
"_" +
std::to_string(i)));
7133 bool simple_wallet::submit_transfer(
const std::vector<std::string> &args_)
7135 if (m_wallet->key_on_device())
7140 if (!try_connect_to_daemon())
7145 std::vector<tools::wallet2::pending_tx> ptx_vector;
7146 bool r = m_wallet->load_tx(
"signed_electroneum_tx", ptx_vector, [&](
const tools::wallet2::signed_tx_set &tx){
return accept_loaded_tx(tx); });
7153 commit_or_save(ptx_vector,
false);
7155 catch (
const std::exception& e)
7157 handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
7170 std::vector<std::string> local_args = args_;
7172 if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() !=
hw::device::TREZOR)
7177 if(local_args.size() != 1) {
7192 std::vector<crypto::secret_key> additional_tx_keys;
7194 bool found_tx_key = m_wallet->get_tx_key(txid, tx_key, additional_tx_keys);
7199 for (
size_t i = 0; i < additional_tx_keys.size(); ++i)
7211 bool simple_wallet::set_tx_key(
const std::vector<std::string> &args_)
7213 std::vector<std::string> local_args = args_;
7215 if(local_args.size() != 2) {
7228 std::vector<crypto::secret_key> additional_tx_keys;
7238 local_args[1] = local_args[1].substr(64);
7239 if (local_args[1].
empty())
7241 additional_tx_keys.resize(additional_tx_keys.size() + 1);
7249 catch (
const std::out_of_range &e)
7259 m_wallet->set_tx_key(txid, tx_key, additional_tx_keys);
7262 catch (
const std::exception &e)
7269 bool simple_wallet::get_tx_proof(
const std::vector<std::string> &args)
7271 if (args.size() != 2 && args.size() != 3)
7295 std::string sig_str = m_wallet->get_tx_proof(txid,
info.address,
info.is_subaddress, args.size() == 3 ? args[2] :
"");
7296 const std::string filename =
"electroneum_tx_proof";
7302 catch (
const std::exception &e)
7309 bool simple_wallet::check_tx_key(
const std::vector<std::string> &args_)
7311 std::vector<std::string> local_args = args_;
7313 if(local_args.size() != 3) {
7318 if (!try_connect_to_daemon())
7334 std::vector<crypto::secret_key> additional_tx_keys;
7340 local_args[1] = local_args[1].substr(64);
7341 while (!local_args[1].
empty())
7343 additional_tx_keys.resize(additional_tx_keys.size() + 1);
7349 local_args[1] = local_args[1].substr(64);
7364 m_wallet->check_tx_key(txid, tx_key, additional_tx_keys,
info.address, received, in_pool, confirmations);
7371 success_msg_writer() <<
tr(
"WARNING: this transaction is not yet included in the blockchain!");
7377 success_msg_writer() << boost::format(
tr(
"This transaction has %u confirmations")) % confirmations;
7390 catch (
const std::exception &e)
7399 if(args.size() != 3 && args.size() != 4) {
7404 if (!try_connect_to_daemon())
7436 if (m_wallet->check_tx_proof(txid,
info.address,
info.is_subaddress, args.size() == 4 ? args[3] :
"", sig_str, received, in_pool, confirmations))
7444 success_msg_writer() <<
tr(
"WARNING: this transaction is not yet included in the blockchain!");
7450 success_msg_writer() << boost::format(
tr(
"This transaction has %u confirmations")) % confirmations;
7468 catch (
const std::exception &e)
7475 bool simple_wallet::get_spend_proof(
const std::vector<std::string> &args)
7477 if (m_wallet->key_on_device())
7482 if(args.size() != 1 && args.size() != 2) {
7487 if (m_wallet->watch_only())
7500 if (!try_connect_to_daemon())
7507 const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] :
"");
7508 const std::string filename =
"electroneum_spend_proof";
7514 catch (
const std::exception &e)
7521 bool simple_wallet::check_spend_proof(
const std::vector<std::string> &args)
7523 if(args.size() != 2 && args.size() != 3) {
7535 if (!try_connect_to_daemon())
7547 if (m_wallet->check_spend_proof(txid, args.size() == 3 ? args[2] :
"", sig_str))
7552 catch (
const std::exception& e)
7559 bool simple_wallet::get_reserve_proof(
const std::vector<std::string> &args)
7561 if (m_wallet->key_on_device())
7566 if(args.size() != 1 && args.size() != 2) {
7571 if (m_wallet->watch_only() || m_wallet->multisig())
7573 fail_msg_writer() <<
tr(
"The reserve proof can be generated only by a full wallet");
7577 boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
7578 if (args[0] !=
"all")
7580 account_minreserve = std::pair<uint32_t, uint64_t>();
7581 account_minreserve->first = m_current_subaddress_account;
7589 if (!try_connect_to_daemon())
7596 const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] :
"");
7597 const std::string filename =
"electroneum_reserve_proof";
7603 catch (
const std::exception &e)
7610 bool simple_wallet::check_reserve_proof(
const std::vector<std::string> &args)
7612 if(args.size() != 2 && args.size() != 3) {
7617 if (!try_connect_to_daemon())
7626 if (
info.is_subaddress)
7644 if (m_wallet->check_reserve_proof(
info.address, args.size() == 3 ? args[2] :
"", sig_str, total, spent))
7653 catch (
const std::exception& e)
7660 static std::string get_human_readable_timespan(std::chrono::seconds seconds)
7669 if (ts < 3600 * 24 * 30.5)
7671 if (ts < 3600 * 24 * 365.25)
7673 return sw::tr(
"a long time");
7677 bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vector<transfer_view>& transfers)
7684 bool coinbase =
true;
7685 bool pub_blockchain_migration =
true;
7686 bool sc_migration =
true;
7691 if (local_args.size() > 0) {
7692 if (local_args[0] ==
"in" || local_args[0] ==
"incoming") {
7693 out =
pending = failed = pub_blockchain_migration = sc_migration =
false;
7694 local_args.erase(local_args.begin());
7696 else if (local_args[0] ==
"out" || local_args[0] ==
"outgoing") {
7697 in = pool = coinbase =
false;
7698 local_args.erase(local_args.begin());
7700 else if (local_args[0] ==
"pending") {
7701 in =
out = failed = coinbase = pub_blockchain_migration = sc_migration =
false;
7702 local_args.erase(local_args.begin());
7704 else if (local_args[0] ==
"failed") {
7705 in =
out =
pending = pool = coinbase = pub_blockchain_migration = sc_migration =
false;
7706 local_args.erase(local_args.begin());
7708 else if (local_args[0] ==
"pool") {
7709 in =
out =
pending = failed = coinbase = pub_blockchain_migration = sc_migration =
false;
7710 local_args.erase(local_args.begin());
7712 else if (local_args[0] ==
"coinbase") {
7713 in =
out =
pending = failed = pool = pub_blockchain_migration = sc_migration =
false;
7715 local_args.erase(local_args.begin());
7717 else if (local_args[0] ==
"migration") {
7718 in =
out =
pending = failed = pool = sc_migration =
false;
7720 local_args.erase(local_args.begin());
7722 else if (local_args[0] ==
"sc_migration") {
7723 in =
out =
pending = failed = pool = pub_blockchain_migration =
false;
7725 local_args.erase(local_args.begin());
7727 else if (local_args[0] ==
"all" || local_args[0] ==
"both") {
7728 local_args.erase(local_args.begin());
7733 std::set<uint32_t> subaddr_indices;
7734 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
7736 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
7738 local_args.erase(local_args.begin());
7742 if (local_args.size() > 0 && local_args[0].find(
'=') == std::string::npos) {
7744 min_height = boost::lexical_cast<
uint64_t>(local_args[0]);
7746 catch (
const boost::bad_lexical_cast &) {
7750 local_args.erase(local_args.begin());
7754 if (local_args.size() > 0 && local_args[0].find(
'=') == std::string::npos) {
7756 max_height = boost::lexical_cast<
uint64_t>(local_args[0]);
7758 catch (
const boost::bad_lexical_cast &) {
7762 local_args.erase(local_args.begin());
7765 const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
7767 if (in || coinbase) {
7768 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
7769 m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7770 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7775 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7776 payment_id = payment_id.substr(0,16);
7784 locked_msg =
"locked";
7789 if (bh >= last_block_height)
7800 transfers.push_back({
7820 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
7821 m_wallet->get_payments_out(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
7822 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7826 std::vector<std::pair<std::string, uint64_t>> destinations;
7831 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7832 payment_id = payment_id.substr(0,16);
7834 transfers.push_back({
7852 if(pub_blockchain_migration) {
7854 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> migration_payments;
7855 m_wallet->get_payments_out_migration(migration_payments, min_height, max_height, m_current_subaddress_account,
7857 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = migration_payments.begin();
7858 i != migration_payments.end(); ++i) {
7862 std::vector<std::pair<std::string, uint64_t>> destinations;
7864 destinations.push_back(
7868 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7869 payment_id = payment_id.substr(0, 16);
7871 transfers.push_back({
7891 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> sc_migration_payments;
7892 m_wallet->get_payments_out_sc_migration(sc_migration_payments, min_height, max_height,
7893 m_current_subaddress_account, subaddr_indices);
7894 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = sc_migration_payments.begin();
7895 i != sc_migration_payments.end(); ++i) {
7899 std::vector<std::pair<std::string, uint64_t>> destinations;
7901 destinations.push_back(
7905 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7906 payment_id = payment_id.substr(0, 16);
7908 transfers.push_back({
7929 m_in_manual_refresh.store(
true, std::memory_order_relaxed);
7932 m_wallet->update_pool_state();
7933 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
7934 m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
7935 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
7938 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7939 payment_id = payment_id.substr(0,16);
7943 if (i->second.m_double_spend_seen)
7944 double_spend_note =
tr(
"[Double spend seen on the network: this transaction may or may not end up being mined] ");
7945 if (i->second.m_nonexistent_utxo_seen)
7946 success_msg_writer() <<
tr(
"Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
7947 transfers.push_back({
7959 note + double_spend_note,
7964 catch (
const std::exception& e)
7972 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
7973 m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account, subaddr_indices);
7974 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
7978 std::vector<std::pair<std::string, uint64_t>> destinations;
7979 for (
const auto &d: pd.
m_dests) {
7983 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
7984 payment_id = payment_id.substr(0,16);
7987 if ((failed && is_failed) || (!is_failed &&
pending)) {
7988 transfers.push_back({
7989 (is_failed ?
"failed" :
"pending"),
7990 (is_failed ?
"failed" :
"pending"),
8007 std::sort(transfers.begin(), transfers.end(), [](
const transfer_view&
a,
const transfer_view& b) ->
bool {
8008 if (
a.confirmed && !b.confirmed)
8010 if (
a.block == b.block)
8011 return a.timestamp < b.timestamp;
8012 return a.block < b.block;
8018 bool simple_wallet::show_transfers(
const std::vector<std::string> &args_)
8020 std::vector<std::string> local_args = args_;
8022 if(local_args.size() > 6) {
8023 fail_msg_writer() <<
tr(
"usage: show_transfers [in|out|pub_blockchain_migration|sc_migration|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
8029 std::vector<transfer_view> all_transfers;
8031 if (!get_transfers(local_args, all_transfers))
8036 for (
const auto& transfer : all_transfers)
8041 if (!transfer.outputs.empty())
8044 for (
const auto& output : transfer.outputs)
8046 if (!destinations.empty())
8047 destinations +=
", ";
8048 destinations += (transfer.direction ==
"in" ? output.first.substr(0, 6) : output.first) +
":" +
print_etn(output.second);
8052 auto formatter = boost::format(
"%8.8llu %12.12s %8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s");
8054 message_writer(color,
false) << formatter
8056 % transfer.direction
8061 % transfer.payment_id
8064 % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](
uint32_t i) { return std::to_string(i); }),
", ")
8071 bool simple_wallet::export_transfers(
const std::vector<std::string>& args_)
8073 std::vector<std::string> local_args = args_;
8075 if(local_args.size() > 7) {
8076 fail_msg_writer() <<
tr(
"usage: export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]");
8082 std::vector<transfer_view> all_transfers;
8085 if (!get_transfers(local_args, all_transfers))
8089 std::string filename = (boost::format(
"output%u.csv") % m_current_subaddress_account).str();
8090 if (local_args.size() > 0 && local_args[0].substr(0, 7) ==
"output=")
8092 filename = local_args[0].substr(7, -1);
8093 local_args.erase(local_args.begin());
8096 std::ofstream file(filename);
8100 boost::format(
"%8.8s,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s") %
8101 tr(
"block") %
tr(
"direction") %
tr(
"unlocked") %
tr(
"timestamp") %
tr(
"amount") %
tr(
"running balance") %
tr(
"hash") %
tr(
"payment ID") %
tr(
"fee") %
tr(
"destination") %
tr(
"amount") %
tr(
"index") %
tr(
"note")
8105 auto formatter = boost::format(
"%8.8llu,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s");
8107 for (
const auto& transfer : all_transfers)
8110 if (transfer.confirmed)
8112 if (transfer.direction ==
"in" || transfer.direction ==
"block")
8113 running_balance += transfer.amount;
8115 running_balance -= transfer.amount + transfer.fee;
8120 % transfer.direction
8126 % transfer.payment_id
8128 % (transfer.outputs.size() ? transfer.outputs[0].first :
"-")
8129 % (transfer.outputs.size() ?
print_etn(transfer.outputs[0].second) :
"")
8134 for (
size_t i = 1; i < transfer.outputs.size(); ++i)
8146 % transfer.outputs[i].first
8160 bool simple_wallet::unspent_outputs(
const std::vector<std::string> &args_)
8162 if(args_.size() > 3)
8167 auto local_args = args_;
8169 std::set<uint32_t> subaddr_indices;
8170 if (local_args.size() > 0 && local_args[0].substr(0, 6) ==
"index=")
8172 if (!parse_subaddress_indices(local_args[0], subaddr_indices))
8174 local_args.erase(local_args.begin());
8178 uint64_t max_amount = std::numeric_limits<uint64_t>::max();
8179 if (local_args.size() > 0)
8186 local_args.erase(local_args.begin());
8187 if (local_args.size() > 0)
8194 local_args.erase(local_args.begin());
8196 if (min_amount > max_amount)
8203 m_wallet->get_transfers(transfers);
8204 std::map<uint64_t, tools::wallet2::transfer_container> amount_to_tds;
8205 uint64_t min_height = std::numeric_limits<uint64_t>::max();
8207 uint64_t found_min_amount = std::numeric_limits<uint64_t>::max();
8210 for (
const auto& td : transfers)
8213 if (td.m_spent || amount < min_amount || amount > max_amount || td.m_subaddr_index.major != m_current_subaddress_account || (subaddr_indices.count(td.m_subaddr_index.minor) == 0 && !subaddr_indices.empty()))
8215 amount_to_tds[amount].push_back(td);
8216 if (min_height > td.m_block_height) min_height = td.m_block_height;
8217 if (max_height < td.m_block_height) max_height = td.m_block_height;
8218 if (found_min_amount > amount) found_min_amount = amount;
8219 if (found_max_amount < amount) found_max_amount = amount;
8222 if (amount_to_tds.empty())
8227 for (
const auto& amount_tds : amount_to_tds)
8229 auto& tds = amount_tds.second;
8231 for (
size_t i = 0; i < tds.size(); )
8233 std::ostringstream oss;
8234 for (
size_t j = 0; j < 8 && i < tds.size(); ++i, ++j)
8235 oss << tds[i].m_block_height <<
tr(
" ");
8240 <<
tr(
"\nMin block height: ") << min_height
8241 <<
tr(
"\nMax block height: ") << max_height
8242 <<
tr(
"\nMin amount found: ") <<
print_etn(found_min_amount)
8243 <<
tr(
"\nMax amount found: ") <<
print_etn(found_max_amount)
8244 <<
tr(
"\nTotal count: ") <<
count;
8245 const size_t histogram_height = 10;
8246 const size_t histogram_width = 50;
8247 double bin_size = (max_height - min_height + 1.0) / histogram_width;
8248 size_t max_bin_count = 0;
8249 std::vector<size_t> histogram(histogram_width, 0);
8250 for (
const auto& amount_tds : amount_to_tds)
8252 for (
auto& td : amount_tds.second)
8254 uint64_t bin_index = (td.m_block_height - min_height + 1) / bin_size;
8255 if (bin_index >= histogram_width)
8256 bin_index = histogram_width - 1;
8257 histogram[bin_index]++;
8258 if (max_bin_count < histogram[bin_index])
8259 max_bin_count = histogram[bin_index];
8262 for (
size_t x = 0; x < histogram_width; ++x)
8264 double bin_count = histogram[x];
8265 if (max_bin_count > histogram_height)
8266 bin_count *= histogram_height / (double)max_bin_count;
8267 if (histogram[x] > 0 && bin_count < 1.0)
8269 histogram[x] = bin_count;
8271 std::vector<std::string> histogram_line(histogram_height,
std::string(histogram_width,
' '));
8272 for (
size_t y = 0; y < histogram_height; ++y)
8274 for (
size_t x = 0; x < histogram_width; ++x)
8276 if (y < histogram[x])
8277 histogram_line[y][x] =
'*';
8280 double count_per_star = max_bin_count / (double)histogram_height;
8281 if (count_per_star < 1)
8284 <<
tr(
"\nBin size: ") << bin_size
8285 <<
tr(
"\nOutputs per *: ") << count_per_star;
8286 ostringstream histogram_str;
8287 histogram_str <<
tr(
"count\n ^\n");
8288 for (
size_t y = histogram_height; y > 0; --y)
8289 histogram_str <<
tr(
" |") << histogram_line[y - 1] <<
tr(
"|\n");
8291 <<
tr(
" +") <<
std::string(histogram_width,
'-') <<
tr(
"+--> block height\n")
8293 <<
tr(
" ") << min_height <<
std::string(histogram_width - 8,
' ') << max_height;
8298 bool simple_wallet::rescan_blockchain(
const std::vector<std::string> &args_)
8301 ResetType reset_type = ResetSoft;
8305 if (args_[0] ==
"hard")
8307 reset_type = ResetHard;
8309 else if (args_[0] ==
"soft")
8311 reset_type = ResetSoft;
8313 else if (args_[0] ==
"keep_ki")
8315 reset_type = ResetSoftKeepKI;
8323 if (args_.size() > 1)
8327 start_height = boost::lexical_cast<
uint64_t>( args_[1] );
8329 catch(
const boost::bad_lexical_cast &)
8336 if (reset_type == ResetHard)
8338 message_writer() <<
tr(
"Warning: this will lose any information which can not be recovered from the blockchain.");
8339 message_writer() <<
tr(
"This includes destination addresses, tx secret keys, tx notes, etc");
8340 std::string confirm = input_line(
tr(
"Rescan anyway?"),
true);
8348 const uint64_t wallet_from_height = m_wallet->get_refresh_from_block_height();
8349 if (start_height > wallet_from_height)
8351 message_writer() <<
tr(
"Warning: your restore height is higher than wallet restore height: ") << wallet_from_height;
8352 std::string confirm = input_line(
tr(
"Rescan anyway ? (Y/Yes/N/No): "));
8360 return refresh_main(start_height, reset_type,
true);
8363 void simple_wallet::check_for_messages()
8367 std::vector<mms::message> new_messages;
8368 bool new_message = get_message_store().
check_for_messages(get_multisig_wallet_state(), new_messages);
8372 list_mms_messages(new_messages);
8379 void simple_wallet::wallet_idle_thread()
8383 boost::unique_lock<boost::mutex> lock(m_idle_mutex);
8384 if (!m_idle_run.load(std::memory_order_relaxed))
8388 if (m_auto_refresh_enabled)
8390 m_auto_refresh_refreshing =
true;
8395 if (try_connect_to_daemon(
true))
8396 m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, fetched_blocks, received_etn,
false);
8399 m_auto_refresh_refreshing =
false;
8405 if (m_auto_refresh_enabled && get_message_store().
get_active())
8407 check_for_messages();
8410 if (!m_idle_run.load(std::memory_order_relaxed))
8412 m_idle_cond.wait_for(lock, boost::chrono::seconds(90));
8418 std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
8420 if (!m_wallet->check_connection(NULL))
8421 prompt +=
tr(
" (no daemon)");
8422 else if (!m_wallet->is_synced())
8423 prompt +=
tr(
" (out of sync)");
8431 try_connect_to_daemon();
8434 refresh_main(0, ResetNone,
true);
8436 m_auto_refresh_enabled = m_wallet->auto_refresh();
8439 m_idle_thread = boost::thread([&]{wallet_idle_thread();});
8444 return m_cmd_binder.
run_handling([
this](){
return get_prompt();},
"");
8452 bool simple_wallet::account(
const std::vector<std::string> &args)
8471 std::vector<std::string> local_args = args;
8473 local_args.erase(local_args.begin());
8474 if (command ==
"new")
8479 label =
tr(
"(Untitled account)");
8480 m_wallet->add_subaddress_account(label);
8481 m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8486 else if(command ==
"generate" && local_args.size() == 1)
8501 uint64_t startAccountsCount = m_wallet->get_num_subaddress_accounts();
8504 m_wallet->add_subaddress_account(
"(Untitled account " +
std::to_string(startAccountsCount + i+1) +
" )",
false);
8507 success_msg_writer() <<
"Generating Accounts... (" << startAccountsCount + i+1 <<
"/" << startAccountsCount+
count <<
")";
8510 m_wallet->add_subaddress_account(
"(Untitled account " +
std::to_string(startAccountsCount +
count) +
" )",
true);
8512 m_current_subaddress_account = m_wallet->get_num_subaddress_accounts() - 1;
8514 catch (
const std::exception& e)
8520 else if (command ==
"switch" && local_args.size() == 1)
8529 if (index_major >= m_wallet->get_num_subaddress_accounts())
8531 fail_msg_writer() <<
tr(
"specify an index between 0 and ") << (m_wallet->get_num_subaddress_accounts() - 1);
8534 m_current_subaddress_account = index_major;
8538 else if (command ==
"label" && local_args.size() >= 1)
8547 local_args.erase(local_args.begin());
8551 m_wallet->set_subaddress_label({index_major, 0}, label);
8555 catch (
const std::exception& e)
8560 else if (command ==
"tag" && local_args.size() >= 2)
8563 std::set<uint32_t> account_indices;
8564 for (
size_t i = 1; i < local_args.size(); ++i)
8572 account_indices.insert(account_index);
8576 m_wallet->set_account_tag(account_indices, tag);
8577 print_accounts(tag);
8579 catch (
const std::exception& e)
8584 else if (command ==
"untag" && local_args.size() >= 1)
8586 std::set<uint32_t> account_indices;
8587 for (
size_t i = 0; i < local_args.size(); ++i)
8595 account_indices.insert(account_index);
8599 m_wallet->set_account_tag(account_indices,
"");
8602 catch (
const std::exception& e)
8607 else if (command ==
"tag_description" && local_args.size() >= 1)
8611 if (local_args.size() > 1)
8613 local_args.erase(local_args.begin());
8614 description = boost::join(local_args,
" ");
8618 m_wallet->set_account_tag_description(tag, description);
8619 print_accounts(tag);
8621 catch (
const std::exception& e)
8633 void simple_wallet::print_accounts()
8635 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8636 size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
8637 for (
const std::pair<std::string, std::string>& p : account_tags.first)
8640 print_accounts(tag);
8641 num_untagged_accounts -=
std::count(account_tags.second.begin(), account_tags.second.end(), tag);
8645 if (num_untagged_accounts > 0)
8648 if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) {
8649 if (m_wallet->balance_all(
false)) {
8651 <<
tr(
", pre v10 unlocked balance: ")
8652 <<
print_etn(m_wallet->unlocked_balance_all(
false));
8655 <<
tr(
", unlocked balance: ") <<
print_etn(m_wallet->unlocked_balance_all(
true));
8659 void simple_wallet::print_accounts(
const std::string& tag)
8661 const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
8668 if (account_tags.first.count(tag) == 0)
8677 uint64_t total_balance = 0, total_unlocked_balance = 0, total_balance_public_chain = 0, total_unlocked_balance_public_chain = 0;
8678 bool found_old_bal =
false;
8680 for (
uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8681 if (account_tags.second[account_index] != tag)
8683 if (m_wallet->balance(account_index,
false)) {
8684 found_old_bal =
true;
8689 if (found_old_bal) {
8692 tr(
"Pre V10 Unlocked balance") %
tr(
"Label");
8693 for (
uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8694 if (account_tags.second[account_index] != tag)
8697 % (m_current_subaddress_account == account_index ?
'*' :
' ')
8699 % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8700 %
print_etn(m_wallet->balance(account_index,
false))
8701 %
print_etn(m_wallet->unlocked_balance(account_index,
false))
8702 % m_wallet->get_subaddress_label({account_index, 0});
8703 total_balance += m_wallet->balance(account_index,
false);
8704 total_unlocked_balance += m_wallet->unlocked_balance(account_index,
false);
8707 <<
tr(
"----------------------------------------------------------------------------------");
8714 if(m_wallet->synced_to_v10()) {
8717 tr(
"Unlocked balance") %
tr(
"Label");
8718 for (
uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) {
8719 if (account_tags.second[account_index] != tag)
8722 % (m_current_subaddress_account == account_index ?
'*' :
' ')
8724 % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
8725 %
print_etn(m_wallet->balance(account_index,
true))
8726 %
print_etn(m_wallet->unlocked_balance(account_index,
true))
8727 % m_wallet->get_subaddress_label({account_index, 0});
8728 total_balance_public_chain += m_wallet->balance(account_index,
true);
8729 total_unlocked_balance_public_chain += m_wallet->unlocked_balance(account_index,
true);
8732 <<
tr(
"----------------------------------------------------------------------------------");
8734 print_etn(total_unlocked_balance_public_chain);
8739 bool simple_wallet::print_address(
const std::vector<std::string> &args)
8748 std::vector<std::string> local_args = args;
8750 m_wallet->get_transfers(transfers);
8752 auto print_address_sub = [
this, &transfers](
uint32_t index)
8754 bool used = std::find_if(
8755 transfers.begin(), transfers.end(),
8758 }) != transfers.end();
8759 success_msg_writer() << index <<
" " << m_wallet->get_subaddress_as_str({m_current_subaddress_account, index}) <<
" " << (index == 0 ?
tr(
"Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) <<
" " << (used ?
tr(
"(used)") :
"");
8763 if (local_args.empty())
8765 print_address_sub(index);
8767 else if (local_args.size() == 1 && local_args[0] ==
"all")
8769 local_args.erase(local_args.begin());
8770 for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
8771 print_address_sub(index);
8773 else if (local_args[0] ==
"new")
8775 local_args.erase(local_args.begin());
8777 if (local_args.size() > 0)
8778 label = boost::join(local_args,
" ");
8780 label =
tr(
"(Untitled address)");
8781 m_wallet->add_subaddress(m_current_subaddress_account, label);
8782 print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8784 else if (local_args.size() >= 2 && local_args[0] ==
"label")
8791 if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8793 fail_msg_writer() <<
tr(
"specify an index between 0 and ") << (m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
8796 local_args.erase(local_args.begin());
8797 local_args.erase(local_args.begin());
8799 m_wallet->set_subaddress_label({m_current_subaddress_account, index}, label);
8800 print_address_sub(index);
8804 local_args.erase(local_args.begin());
8807 if (local_args.size() > 0)
8814 local_args.erase(local_args.begin());
8816 if (index_max < index_min)
8817 std::swap(index_min, index_max);
8818 if (index_min >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8823 if (index_max >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
8825 message_writer() <<
tr(
"<index_max> exceeds the bound");
8826 index_max = m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1;
8828 for (index = index_min; index <= index_max; ++index)
8829 print_address_sub(index);
8839 bool simple_wallet::print_integrated_address(
const std::vector<std::string> &args)
8842 if (args.size() > 1)
8847 if (args.size() == 0)
8849 if (m_current_subaddress_account != 0)
8851 fail_msg_writer() <<
tr(
"Integrated addresses can only be created for account 0");
8854 payment_id = crypto::rand<crypto::hash8>();
8856 success_msg_writer() <<
tr(
"Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8861 if (m_current_subaddress_account != 0)
8863 fail_msg_writer() <<
tr(
"Integrated addresses can only be created for account 0");
8866 success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
8873 if (
info.has_payment_id)
8889 bool simple_wallet::address_book(
const std::vector<std::string> &args)
8891 if (args.size() == 0)
8894 else if (args.size() == 1 || (args[0] !=
"add" && args[0] !=
"delete"))
8899 else if (args[0] ==
"add")
8908 size_t description_start = 2;
8909 if (
info.has_payment_id)
8911 memcpy(payment_id.data,
info.payment_id.data, 8);
8913 else if (!
info.has_payment_id && args.size() >= 4 && args[2] ==
"pid")
8918 description_start += 2;
8922 fail_msg_writer() <<
tr(
"Short payment IDs are to be used within an integrated address only");
8932 for (
size_t i = description_start; i < args.size(); ++i)
8934 if (i > description_start)
8936 description += args[i];
8938 m_wallet->add_address_book_row(
info.address, payment_id, description,
info.is_subaddress);
8948 m_wallet->delete_address_book_row(row_id);
8950 auto address_book = m_wallet->get_address_book();
8951 if (address_book.empty())
8957 for (
size_t i = 0; i < address_book.size(); ++i) {
8958 auto& row = address_book[i];
8968 bool simple_wallet::set_tx_note(
const std::vector<std::string> &args)
8970 if (args.size() == 0)
8985 for (
size_t n = 1; n < args.size(); ++n)
8991 m_wallet->set_tx_note(txid, note);
8996 bool simple_wallet::get_tx_note(
const std::vector<std::string> &args)
8998 if (args.size() != 1)
9021 bool simple_wallet::set_description(
const std::vector<std::string> &args)
9026 for (
size_t n = 0; n < args.size(); ++n)
9030 description += args[n];
9032 m_wallet->set_description(description);
9037 bool simple_wallet::get_description(
const std::vector<std::string> &args)
9039 if (args.size() != 0)
9045 std::string description = m_wallet->get_description();
9046 if (description.empty())
9054 bool simple_wallet::status(
const std::vector<std::string> &args)
9056 uint64_t local_height = m_wallet->get_blockchain_current_height();
9059 if (!m_wallet->check_connection(&
version, &ssl))
9066 uint64_t bc_height = get_daemon_blockchain_height(err);
9069 bool synced = local_height == bc_height;
9070 success_msg_writer() <<
"Refreshed " << local_height <<
"/" << bc_height <<
", " << (synced ?
"synced" :
"syncing")
9071 <<
", daemon RPC v" << get_version_string(
version) <<
", " << (ssl ?
"SSL" :
"no SSL");
9075 fail_msg_writer() <<
"Refreshed " << local_height <<
"/?, daemon connection error";
9080 bool simple_wallet::wallet_info(
const std::vector<std::string> &args)
9084 std::string description = m_wallet->get_description();
9085 if (description.empty())
9087 description =
"<Not set>";
9089 message_writer() <<
tr(
"Filename: ") << m_wallet->get_wallet_file();
9090 message_writer() <<
tr(
"Description: ") << description;
9091 message_writer() <<
tr(
"Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
9093 if (m_wallet->watch_only())
9094 type =
tr(
"Watch only");
9095 else if (m_wallet->multisig(&ready, &
threshold, &total))
9096 type = (boost::format(
tr(
"%u/%u multisig%s")) %
threshold % total % (ready ?
"" :
" (not yet finalized)")).str();
9098 type =
tr(
"Normal");
9099 message_writer() <<
tr(
"Type: ") << type;
9100 message_writer() <<
tr(
"Network type: ") << (
9106 bool simple_wallet::sign(
const std::vector<std::string> &args)
9108 if (m_wallet->key_on_device())
9113 if (args.size() != 1)
9118 if (m_wallet->watch_only())
9123 if (m_wallet->multisig())
9145 bool simple_wallet::verify(
const std::vector<std::string> &args)
9147 if (args.size() != 3)
9183 bool simple_wallet::export_key_images(
const std::vector<std::string> &args)
9185 if (m_wallet->key_on_device())
9190 if (args.size() != 1)
9195 if (m_wallet->watch_only())
9202 if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9209 if (!m_wallet->export_key_images(filename))
9215 catch (
const std::exception &e)
9217 LOG_ERROR(
"Error exporting key images: " << e.what());
9226 bool simple_wallet::import_key_images(
const std::vector<std::string> &args)
9228 if (m_wallet->key_on_device())
9233 if (!m_wallet->is_trusted_daemon())
9235 fail_msg_writer() <<
tr(
"this command requires a trusted daemon. Enable with --trusted-daemon");
9239 if (args.size() != 1)
9250 uint64_t height = m_wallet->import_key_images(filename, spent, unspent);
9254 catch (
const std::exception &e)
9263 bool simple_wallet::hw_key_images_sync(
const std::vector<std::string> &args)
9265 if (!m_wallet->key_on_device())
9270 if (!m_wallet->get_account().get_device().has_ki_cold_sync())
9277 key_images_sync_intern();
9281 void simple_wallet::key_images_sync_intern(){
9284 message_writer(
console_color_white,
false) <<
tr(
"Please confirm the key image sync on the device");
9291 if (!m_wallet->is_trusted_daemon())
9293 message_writer() <<
tr(
"Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
9303 catch (
const std::exception &e)
9309 bool simple_wallet::hw_reconnect(
const std::vector<std::string> &args)
9311 if (!m_wallet->key_on_device())
9320 bool r = m_wallet->reconnect_device();
9325 catch (
const std::exception &e)
9334 bool simple_wallet::export_outputs(
const std::vector<std::string> &args)
9336 if (m_wallet->key_on_device())
9341 if (args.size() != 1)
9348 if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
9355 std::string data = m_wallet->export_outputs_to_str();
9363 catch (
const std::exception &e)
9365 LOG_ERROR(
"Error exporting outputs: " << e.what());
9374 bool simple_wallet::import_outputs(
const std::vector<std::string> &args)
9376 if (m_wallet->key_on_device())
9381 if (args.size() != 1)
9399 size_t n_outputs = m_wallet->import_outputs_from_str(data);
9402 catch (
const std::exception &e)
9404 fail_msg_writer() <<
"Failed to import outputs " << filename <<
": " << e.what();
9411 bool simple_wallet::show_transfer(
const std::vector<std::string> &args)
9413 if (args.size() != 1)
9427 const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
9429 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
9430 m_wallet->get_payments(payments, 0, (
uint64_t)-1, m_current_subaddress_account);
9431 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
9435 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9436 payment_id = payment_id.substr(0,16);
9446 uint64_t last_block_reward = m_wallet->get_last_block_reward();
9447 uint64_t suggested_threshold = last_block_reward ? (pd.
m_amount + last_block_reward - 1) / last_block_reward : 0;
9448 if (bh >= last_block_height)
9449 success_msg_writer() <<
"Locked: " << (bh - last_block_height) <<
" blocks to unlock";
9450 else if (suggested_threshold > 0)
9470 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
9471 m_wallet->get_payments_out(payments_out, 0, (
uint64_t)-1, m_current_subaddress_account);
9472 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
9473 if (i->first == txid)
9485 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9486 payment_id = payment_id.substr(0,16);
9503 m_wallet->update_pool_state();
9504 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
9505 m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account);
9506 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
9511 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9512 payment_id = payment_id.substr(0,16);
9520 if (i->second.m_double_spend_seen)
9521 success_msg_writer() <<
tr(
"Double spend seen on the network: this transaction may or may not end up being mined");
9522 if (i->second.m_nonexistent_utxo_seen)
9523 success_msg_writer() <<
tr(
"Nonexistent UTXO seen on the network: this transaction may or may not end up being mined");
9533 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
9534 m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account);
9535 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
9536 if (i->first == txid)
9542 if (payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
9543 payment_id = payment_id.substr(0,16);
9546 success_msg_writer() << (is_failed ?
"Failed" :
"Pending") <<
" outgoing transaction found";
9569 if (m_in_manual_refresh.load(std::memory_order_relaxed))
9579 void simple_wallet::commit_or_save(std::vector<tools::wallet2::pending_tx>& ptx_vector,
bool do_not_relay)
9582 while (!ptx_vector.empty())
9584 auto & ptx = ptx_vector.back();
9593 success_msg_writer(
true) <<
tr(
"Transaction successfully saved to ") << filename <<
tr(
", txid ") << txid;
9595 fail_msg_writer() <<
tr(
"Failed to save transaction to ") << filename <<
tr(
", txid ") << txid;
9599 m_wallet->commit_tx(ptx);
9601 <<
tr(
"You can check its status by using the `show_transfers` command.");
9604 ptx_vector.pop_back();
9614 std::locale::global(boost::locale::generator().generate(
""));
9615 boost::filesystem::path::imbue(std::locale());
9618 po::options_description desc_params(
wallet_args::tr(
"Wallet options"));
9646 po::positional_options_description positional_options;
9649 boost::optional<po::variables_map> vm;
9650 bool should_terminate =
false;
9653 "electroneum-wallet-cli [--wallet-file=<filename>|--generate-new-wallet=<filename>] [<COMMAND>]",
9654 sw::tr(
"This is the command line electroneum wallet. It needs to connect to a electroneum\ndaemon to work correctly.\nWARNING: Do not reuse your Electroneum keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."),
9658 "electroneum-wallet-cli.log" 9666 if (should_terminate)
9674 const bool r = w.
init(*vm);
9678 if (!command.empty())
9694 if (type == CTRL_C_EVENT)
9732 bool simple_wallet::user_confirms(
const std::string &question)
9734 std::string answer = input_line(question +
tr(
" (Y/Yes/N/No): "));
9743 number = boost::lexical_cast<
uint32_t>(arg);
9744 valid = (number >= lower_bound) && (number <= upper_bound);
9746 catch(
const boost::bad_lexical_cast &)
9752 bool simple_wallet::choose_mms_processing(
const std::vector<mms::processing_data> &data_list,
uint32_t &
choice)
9754 size_t choices = data_list.size();
9761 message_writer() <<
tr(
"Choose processing:");
9763 for (
size_t i = 0; i < choices; ++i)
9770 text +=
tr(
"Sign tx");
9778 text +=
tr(
"Send the tx for submission to ");
9782 text +=
tr(
"Send the tx for signing to ");
9789 text +=
tr(
"Submit tx");
9792 text +=
tr(
"unknown");
9795 message_writer() << text;
9799 if (std::cin.eof() || line.empty())
9803 bool choice_ok = get_number_from_arg(line,
choice, 1, choices);
9815 void simple_wallet::list_mms_messages(
const std::vector<mms::message> &messages)
9817 message_writer() << boost::format(
"%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
tr(
"Id") %
tr(
"I/O") %
tr(
"Authorized Signer")
9818 %
tr(
"Message Type") %
tr(
"Height") %
tr(
"R") %
tr(
"Message State") %
tr(
"Since");
9821 for (
size_t i = 0; i < messages.size(); ++i)
9827 boost::format(
"%4s %-4s %-30s %-21s %7s %3s %-15s %-40s") %
9839 void simple_wallet::list_signers(
const std::vector<mms::authorized_signer> &signers)
9841 message_writer() << boost::format(
"%2s %-20s %-s") %
tr(
"#") %
tr(
"Label") %
tr(
"Transport Address");
9842 message_writer() << boost::format(
"%2s %-20s %-s") %
"" %
tr(
"Auto-Config Token") %
tr(
"Electroneum Address");
9843 for (
size_t i = 0; i < signers.size(); ++i)
9854 electroneum_address =
tr(
"<not set>");
9857 message_writer() << boost::format(
"%2s %-20s %-s") % (i + 1) % label % transport_address;
9858 message_writer() << boost::format(
"%2s %-20s %-s") %
"" % signer.
auto_config_token % electroneum_address;
9859 message_writer() <<
"";
9863 void simple_wallet::add_signer_config_messages()
9869 const std::vector<mms::authorized_signer> signers = ms.
get_all_signers();
9872 for (
uint32_t i = 1 ; i < num_authorized_signers; ++i)
9878 void simple_wallet::show_message(
const mms::message &m)
9882 bool display_content;
9889 display_content =
true;
9893 display_content =
false;
9896 message_writer() <<
"";
9897 message_writer() <<
tr(
"Message ") << m.
id;
9900 message_writer() <<
tr(
"State: ") << boost::format(
tr(
"%s since %s, %s ago")) %
9904 message_writer() <<
tr(
"Sent: Never");
9908 message_writer() << boost::format(
tr(
"Sent: %s, %s ago")) %
9912 message_writer() <<
tr(
"Content size: ") << m.
content.length() <<
tr(
" bytes");
9913 message_writer() <<
tr(
"Content: ") << (display_content ? sanitized_text :
tr(
"(binary data)"));
9924 void simple_wallet::ask_send_all_ready_messages()
9927 std::vector<mms::message> ready_messages;
9929 for (
size_t i = 0; i < messages.size(); ++i)
9934 ready_messages.push_back(m);
9937 if (ready_messages.size() != 0)
9939 list_mms_messages(ready_messages);
9943 send = user_confirms(
tr(
"Send these messages now?"));
9948 for (
size_t i = 0; i < ready_messages.size(); ++i)
9961 bool valid_id =
false;
9965 id = (
uint32_t)boost::lexical_cast<uint32_t>(arg);
9968 catch (
const boost::bad_lexical_cast &)
9978 void simple_wallet::mms_init(
const std::vector<std::string> &args)
9980 if (args.size() != 3)
9982 fail_msg_writer() <<
tr(
"usage: mms init <required_signers>/<authorized_signers> <own_label> <own_transport_address>");
9988 if (!user_confirms(
tr(
"The MMS is already initialized. Re-initialize by deleting all signer info and messages?")))
9996 std::vector<std::string> numbers;
9997 boost::split(numbers, mn, boost::is_any_of(
"/"));
9998 bool mn_ok = (numbers.size() == 2)
9999 && get_number_from_arg(numbers[1], num_authorized_signers, 2, 100)
10000 && get_number_from_arg(numbers[0], num_required_signers, 2, num_authorized_signers);
10003 fail_msg_writer() <<
tr(
"Error in the number of required signers and/or authorized signers");
10007 ms.
init(get_multisig_wallet_state(), args[1], args[2], num_authorized_signers, num_required_signers);
10010 void simple_wallet::mms_info(
const std::vector<std::string> &args)
10015 message_writer() << boost::format(
"The MMS is active for %s/%s multisig.")
10020 message_writer() <<
tr(
"The MMS is not active.");
10024 void simple_wallet::mms_signer(
const std::vector<std::string> &args)
10027 const std::vector<mms::authorized_signer> &signers = ms.
get_all_signers();
10028 if (args.size() == 0)
10031 list_signers(signers);
10046 if ((args.size() < 2) || (args.size() > 4))
10048 fail_msg_writer() <<
tr(
"mms signer [<number> <label> [<transport_address> [<electroneum_address>]]]");
10052 boost::optional<string> label = args[1];
10053 boost::optional<string> transport_address;
10054 if (args.size() >= 3)
10056 transport_address = args[2];
10058 boost::optional<cryptonote::account_public_address> electroneum_address;
10061 if (args.size() == 4)
10070 electroneum_address =
info.address;
10072 if ((messages.size() > 0) ||
state.multisig)
10074 fail_msg_writer() <<
tr(
"Wallet state does not allow changing Electroneum addresses anymore");
10078 ms.
set_signer(
state, index, label, transport_address, electroneum_address);
10081 void simple_wallet::mms_list(
const std::vector<std::string> &args)
10084 if (args.size() != 0)
10091 list_mms_messages(messages);
10094 void simple_wallet::mms_next(
const std::vector<std::string> &args)
10097 if ((args.size() > 1) || ((args.size() == 1) && (args[0] !=
"sync")))
10102 bool avail =
false;
10103 std::vector<mms::processing_data> data_list;
10104 bool force_sync =
false;
10108 if ((args.size() == 1) && (args[0] ==
"sync"))
10114 string wait_reason;
10120 avail = choose_mms_processing(data_list,
choice);
10122 else if (!wait_reason.empty())
10124 message_writer() <<
tr(
"No next step: ") << wait_reason;
10130 bool command_successful =
false;
10134 message_writer() <<
tr(
"prepare_multisig");
10135 command_successful = prepare_multisig_main(std::vector<std::string>(),
true);
10140 message_writer() <<
tr(
"make_multisig");
10141 size_t number_of_key_sets = data.
message_ids.size();
10142 std::vector<std::string> sig_args(number_of_key_sets + 1);
10144 for (
size_t i = 0; i < number_of_key_sets; ++i)
10149 command_successful = make_multisig_main(sig_args,
true);
10155 message_writer() <<
tr(
"exchange_multisig_keys");
10156 size_t number_of_key_sets = data.
message_ids.size();
10158 std::vector<std::string> sig_args(number_of_key_sets);
10159 for (
size_t i = 0; i < number_of_key_sets; ++i)
10164 command_successful = exchange_multisig_keys_main(sig_args,
true);
10170 message_writer() <<
tr(
"export_multisig_info");
10171 std::vector<std::string> export_args;
10172 export_args.push_back(
"MMS");
10173 command_successful = export_multisig_main(export_args,
true);
10179 message_writer() <<
tr(
"import_multisig_info");
10180 std::vector<std::string> import_args;
10181 for (
size_t i = 0; i < data.
message_ids.size(); ++i)
10184 import_args.push_back(m.
content);
10186 command_successful = import_multisig_main(import_args,
true);
10192 message_writer() <<
tr(
"sign_multisig");
10193 std::vector<std::string> sign_args;
10195 sign_args.push_back(m.
content);
10196 command_successful = sign_multisig_main(sign_args,
true);
10202 message_writer() <<
tr(
"submit_multisig");
10203 std::vector<std::string> submit_args;
10205 submit_args.push_back(m.
content);
10206 command_successful = submit_multisig_main(submit_args,
true);
10212 message_writer() <<
tr(
"Send tx");
10217 command_successful =
true;
10223 message_writer() <<
tr(
"Process signer config");
10232 std::vector<mms::authorized_signer> signers;
10234 list_signers(signers);
10235 if (!user_confirms(
tr(
"Replace current signer config with the one displayed above?")))
10243 command_successful =
true;
10249 message_writer() <<
tr(
"Process auto config data");
10251 for (
size_t i = 0; i < data.
message_ids.size(); ++i)
10257 add_signer_config_messages();
10258 command_successful =
true;
10263 message_writer() <<
tr(
"Nothing ready to process");
10267 if (command_successful)
10272 ask_send_all_ready_messages();
10278 void simple_wallet::mms_sync(
const std::vector<std::string> &args)
10281 if (args.size() != 0)
10290 message_writer() <<
tr(
"export_multisig_info");
10291 std::vector<std::string> export_args;
10292 export_args.push_back(
"MMS");
10293 export_multisig_main(export_args,
true);
10294 ask_send_all_ready_messages();
10297 void simple_wallet::mms_transfer(
const std::vector<std::string> &args)
10300 transfer_main(
Transfer, args,
true);
10303 void simple_wallet::mms_delete(
const std::vector<std::string> &args)
10305 if (args.size() != 1)
10312 if (args[0] ==
"all")
10314 if (user_confirms(
tr(
"Delete all messages?")))
10322 bool valid_id = get_message_from_arg(args[0], m);
10331 void simple_wallet::mms_send(
const std::vector<std::string> &args)
10333 if (args.size() == 0)
10335 ask_send_all_ready_messages();
10338 else if (args.size() != 1)
10346 bool valid_id = get_message_from_arg(args[0], m);
10353 void simple_wallet::mms_receive(
const std::vector<std::string> &args)
10355 if (args.size() != 0)
10360 std::vector<mms::message> new_messages;
10366 list_mms_messages(new_messages);
10370 void simple_wallet::mms_export(
const std::vector<std::string> &args)
10372 if (args.size() != 1)
10380 bool valid_id = get_message_from_arg(args[0], m);
10383 const std::string filename =
"mms_message_content";
10395 void simple_wallet::mms_note(
const std::vector<std::string> &args)
10398 if (args.size() == 0)
10402 for (
size_t i = 0; i < messages.size(); ++i)
10412 if (args.size() < 2)
10425 for (
size_t n = 1; n < args.size(); ++n)
10436 ask_send_all_ready_messages();
10439 void simple_wallet::mms_show(
const std::vector<std::string> &args)
10441 if (args.size() != 1)
10449 bool valid_id = get_message_from_arg(args[0], m);
10456 void simple_wallet::mms_set(
const std::vector<std::string> &args)
10458 bool set = args.size() == 2;
10459 bool query = args.size() == 1;
10460 if (!
set && !query)
10467 if (args[0] ==
"auto-send")
10472 bool ok = parse_bool(args[1], result);
10484 message_writer() << (ms.
get_auto_send() ?
tr(
"Auto-send is on") :
tr(
"Auto-send is off"));
10493 void simple_wallet::mms_help(
const std::vector<std::string> &args)
10495 if (args.size() > 1)
10500 std::vector<std::string> help_args;
10501 help_args.push_back(
"mms");
10502 if (args.size() == 1)
10504 help_args.push_back(args[0]);
10509 void simple_wallet::mms_send_signer_config(
const std::vector<std::string> &args)
10511 if (args.size() != 0)
10523 add_signer_config_messages();
10524 ask_send_all_ready_messages();
10527 void simple_wallet::mms_start_auto_config(
const std::vector<std::string> &args)
10531 size_t args_size = args.size();
10532 if ((args_size != 0) && (args_size != other_signers))
10539 fail_msg_writer() <<
tr(
"There are signers without a label set. Complete labels before auto-config or specify them as parameters here.");
10545 if (!user_confirms(
tr(
"Auto-config is already running. Cancel and restart?")))
10552 if (args_size != 0)
10555 for (
uint32_t i = 1; i < (other_signers + 1); ++i)
10565 void simple_wallet::mms_stop_auto_config(
const std::vector<std::string> &args)
10567 if (args.size() != 0)
10572 if (!user_confirms(
tr(
"Delete any auto-config tokens and stop auto-config?")))
10581 void simple_wallet::mms_auto_config(
const std::vector<std::string> &args)
10583 if (args.size() != 1)
10598 if (!user_confirms(
tr(
"Auto-config already running. Cancel and restart?")))
10605 ask_send_all_ready_messages();
10608 bool simple_wallet::mms(
const std::vector<std::string> &args)
10612 m_wallet->get_multisig_wallet_state();
10614 catch(
const std::exception &e)
10623 if (args.size() == 0)
10630 std::vector<std::string> mms_args = args;
10631 mms_args.erase(mms_args.begin());
10633 if (sub_command ==
"init")
10635 mms_init(mms_args);
10640 fail_msg_writer() <<
tr(
"The MMS is not active. Activate using the \"mms init\" command");
10643 else if (sub_command ==
"info")
10645 mms_info(mms_args);
10647 else if (sub_command ==
"signer")
10649 mms_signer(mms_args);
10651 else if (sub_command ==
"list")
10653 mms_list(mms_args);
10655 else if (sub_command ==
"next")
10657 mms_next(mms_args);
10659 else if (sub_command ==
"sync")
10661 mms_sync(mms_args);
10663 else if (sub_command ==
"transfer")
10665 mms_transfer(mms_args);
10667 else if (sub_command ==
"delete")
10669 mms_delete(mms_args);
10671 else if (sub_command ==
"send")
10673 mms_send(mms_args);
10675 else if (sub_command ==
"receive")
10677 mms_receive(mms_args);
10679 else if (sub_command ==
"export")
10681 mms_export(mms_args);
10683 else if (sub_command ==
"note")
10685 mms_note(mms_args);
10687 else if (sub_command ==
"show")
10689 mms_show(mms_args);
10691 else if (sub_command ==
"set")
10695 else if (sub_command ==
"help")
10697 mms_help(mms_args);
10699 else if (sub_command ==
"send_signer_config")
10701 mms_send_signer_config(mms_args);
10703 else if (sub_command ==
"start_auto_config")
10705 mms_start_auto_config(mms_args);
10707 else if (sub_command ==
"stop_auto_config")
10709 mms_stop_auto_config(mms_args);
10711 else if (sub_command ==
"auto_config")
10713 mms_auto_config(mms_args);
10724 catch (
const std::exception &e)
const char *const ELECTRONEUM_RELEASE_NAME
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
const config_t & get_config(network_type nettype)
static const char * tr(const char *str)
bool is_no(const std::string &str)
const std::vector< message > & get_all_messages() const
const CharType(& source)[N]
boost::optional< wipeable_string > parse_hexstr() const
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
command_line::arg_descriptor< std::string > arg_wallet_file()
size_t size() const noexcept
std::vector< const Language::Base * > get_language_list()
#define CHECK_SIMPLE_VARIABLE(name, f, help)
void save(Archive &a, const std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
Information representing errors in application but application will keep running. ...
void set_messages_processed(const processing_data &data)
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT
const command_line::arg_descriptor< std::vector< std::string > > arg_command
const std::vector< authorized_signer > & get_all_signers() const
#define HF_VERSION_ENFORCE_0_DECOY_TXS
static const char * message_direction_to_string(message_direction direction)
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
std::string print_etn(uint64_t amount, unsigned int decimal_point)
std::string join_priority_strings(const char *delimiter)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define CRYPTONOTE_MAX_BLOCK_NUMBER
bool check_for_messages(const multisig_wallet_state &state, std::vector< message > &messages)
uint32_t receiving_signer_index
bool is_yes(const std::string &str)
const std::string old_language_name
void mlog_set_log(const char *log)
void set_handler(const std::string &cmd, const callback &hndlr, const std::string &usage="", const std::string &description="")
#define CORE_RPC_STATUS_BUSY
void unpack_signer_config(const multisig_wallet_state &state, const std::string &signer_config, std::vector< authorized_signer > &signers)
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
void delete_all_messages()
SECP256K1_API int secp256k1_ec_pubkey_serialize(const secp256k1_context *ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey *pubkey, unsigned int flags) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
std::vector< uint64_t > key_offsets
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
bool parse_priority(const std::string &arg, uint32_t &priority)
const char * tr(const char *str)
bool check_auto_config_token(const std::string &raw_token, std::string &adjusted_token) const
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
std::string get_unit(unsigned int decimal_point)
bool is_file_exist(const std::string &path)
#define HF_VERSION_ENFORCE_0_DECOY_TXS_END
bool process_command(const std::vector< std::string > &args)
#define SECP256K1_CONTEXT_SIGN
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V6
#define SCOPED_WALLET_UNLOCK()
#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code)
bool get_account_address_from_str_or_url(address_parse_info &info, network_type nettype, const std::string &str_or_url, std::function< std::string(const std::string &, const std::vector< std::string > &, bool)> dns_confirm)
std::vector< uint8_t > extra
bool hex_to_pod(T &pod) const
#define CORE_RPC_STATUS_OK
for(i=1;i< 1;++i) fe_sq(t0
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
#define SECP256K1_EC_UNCOMPRESSED
void send_message(const multisig_wallet_state &state, uint32_t id)
Holds cryptonote related classes and helpers.
struct secp256k1_context_struct secp256k1_context
void process_auto_config_data_message(uint32_t id)
bool parse_amount(uint64_t &amount, const std::string &str_amount_)
Manages wallet operations. This is the most abstracted wallet class.
const char *const ELECTRONEUM_VERSION_FULL
std::string transport_address
std::vector< txin_v > vin
blobdata tx_to_blob(const transaction &tx)
mdb_size_t count(MDB_cursor *cur)
void split(std::vector< wipeable_string > &fields) const
unsigned int get_default_decimal_point()
cryptonote::account_public_address etn_address
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(const secp256k1_context *ctx, const unsigned char *seckey) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2)
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
#define MAKE_CORE_RPC_VERSION(major, minor)
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
cryptonote::simple_wallet sw
std::string mlog_get_categories()
size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token)
void sc_reduce32(unsigned char *)
message_processing processing
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
std::string auto_config_token
bool empty() const noexcept
bool signer_labels_complete() const
void delete_message(uint32_t id)
const authorized_signer & get_signer(uint32_t index) const
unsigned __int64 uint64_t
bool init(const boost::program_options::variables_map &vm)
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
command_line::arg_descriptor< std::string > arg_generate_from_json()
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
bool save_string_to_file(const std::string &path_to_file, const std::string &str)
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const
void get_signer_config(std::string &signer_config)
Header file that declares simple_wallet class.
const base::type::char_t * unit
static const char * message_type_to_string(message_type type)
#define LOCK_IDLE_SCOPE()
uint32_t get_num_required_signers() const
std::string get_english_name_for(const std::string &name)
Returns the name of a language in English.
version
Supported socks variants.
void set_default_decimal_point(unsigned int decimal_point)
void start_auto_config(const multisig_wallet_state &state)
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
This is the orginal cryptonote protocol network-events handler, modified by us.
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
#define PRINT_USAGE(usage_help)
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
bool get_processable_messages(const multisig_wallet_state &state, bool force_sync, std::vector< processing_data > &data_list, std::string &wait_reason)
account_public_address addr
bool get_signer_index_by_label(const std::string label, uint32_t &index) const
static const char * message_state_to_string(message_state state)
expect< void > success() noexcept
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)
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)
Mainly useful to represent current progress of application.
const T & move(const T &t)
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)
bool get_auto_send() const
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
const GenericPointer< typename T::ValueType > T2 value
void * memcpy(void *a, const void *b, size_t c)
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
void process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
crypto::key_image k_image
crypto::hash get_transaction_hash(const transaction &t)
std::string signer_to_string(const authorized_signer &signer, uint32_t max_width)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
bool signer_config_complete() const
Mnemonic seed generation and wallet restoration from them.
T & unwrap(mlocked< T > &src)
bool process_command_vec(const std::vector< std::string > &cmd)
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
bool get_message_by_id(uint32_t id, message &m) const
uint32_t get_num_authorized_signers() const
std::vector< uint32_t > message_ids
expect< void > send(const epee::span< const std::uint8_t > payload, void *const socket, const int flags) noexcept
void set_message_processed_or_sent(uint32_t id)
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 cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height)
int main(int argc, char *argv[])
uint16_t const RPC_DEFAULT_PORT
const command_line::arg_descriptor< std::string > arg_fallback_to_pow_checkpoint_hash
bool secret_key_to_public_key(const secret_key &sec, public_key &pub)
std::string to_string(t_connection_type type)
std::string hex(difficulty_type v)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
constexpr const char ETN_DONATION_ADDR[]
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
bool run_handling(std::function< std::string(void)> prompt, const std::string &usage_string, std::function< void(void)> exit_handler=NULL)
const char * data() const noexcept
#define HF_VERSION_PER_BYTE_FEE
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS
void set_auto_send(bool auto_send)
message_direction direction
#define CATCH_ENTRY_L0(lacation, return_val)
#define LONG_PAYMENT_ID_SUPPORT_CHECK()
error
Tracks LMDB error codes.
void mlog_set_log_level(int level)
SECP256K1_API secp256k1_context * secp256k1_context_create(unsigned int flags) SECP256K1_WARN_UNUSED_RESULT
#define CORE_RPC_VERSION_MAJOR
size_t add_message(const multisig_wallet_state &state, uint32_t signer_index, message_type type, message_direction direction, const std::string &content)
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)
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
void verbose(enum verbosity_value level, const char *format,...) ATTR_FORMAT(printf
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content)