diff options
Diffstat (limited to 'src/simplewallet')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 195 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 2 |
2 files changed, 167 insertions, 30 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 02a099811..24a6eec43 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -92,6 +92,8 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol +#define OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days + #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ @@ -177,8 +179,8 @@ namespace " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n" " account untag <account_index_1> [<account_index_2> ...]\n" " account tag_description <tag_name> <description>"); - 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>]"); - const char* USAGE_INTEGRATED_ADDRESS("integrated_address [<payment_id> | <address>]"); + 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> | device [<index>]]"); + const char* USAGE_INTEGRATED_ADDRESS("integrated_address [device] [<payment_id> | <address>]"); const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]"); const char* USAGE_SET_VARIABLE("set <option> [<value>]"); const char* USAGE_GET_TX_KEY("get_tx_key <txid>"); @@ -199,11 +201,11 @@ namespace const char* USAGE_SET_DESCRIPTION("set_description [free text note]"); const char* USAGE_SIGN("sign <filename>"); const char* USAGE_VERIFY("verify <filename> <address> <signature>"); - const char* USAGE_EXPORT_KEY_IMAGES("export_key_images <filename>"); + const char* USAGE_EXPORT_KEY_IMAGES("export_key_images [all] <filename>"); const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>"); const char* USAGE_HW_KEY_IMAGES_SYNC("hw_key_images_sync"); const char* USAGE_HW_RECONNECT("hw_reconnect"); - const char* USAGE_EXPORT_OUTPUTS("export_outputs <filename>"); + const char* USAGE_EXPORT_OUTPUTS("export_outputs [all] <filename>"); const char* USAGE_IMPORT_OUTPUTS("import_outputs <filename>"); const char* USAGE_SHOW_TRANSFER("show_transfer <txid>"); const char* USAGE_MAKE_MULTISIG("make_multisig <threshold> <string1> [<string>...]"); @@ -801,6 +803,12 @@ bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std: return print_seed(true); } +bool simple_wallet::restore_height(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + success_msg_writer() << m_wallet->get_refresh_from_block_height(); + return true; +} + bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { if (m_wallet->key_on_device()) @@ -2843,6 +2851,9 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display the Electrum-style mnemonic seed")); + m_cmd_binder.set_handler("restore_height", + boost::bind(&simple_wallet::restore_height, this, _1), + tr("Display the restore height")); m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr(USAGE_SET_VARIABLE), @@ -2886,7 +2897,7 @@ simple_wallet::simple_wallet() "segregate-pre-fork-outputs <1|0>\n " " Set this if you intend to spend outputs on both Monero AND a key reusing fork.\n " "key-reuse-mitigation2 <1|0>\n " - " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n" + " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n " "subaddress-lookahead <major>:<minor>\n " " Set the lookahead sizes for the subaddress hash table.\n " " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n " @@ -3128,7 +3139,7 @@ simple_wallet::simple_wallet() tr("Available options:\n " "auto-send <1|0>\n " " Whether to automatically send newly generated messages right away.\n ")); - m_cmd_binder.set_handler("mms send_message_config", + m_cmd_binder.set_handler("mms send_signer_config", boost::bind(&simple_wallet::mms, this, _1), tr(USAGE_MMS_SEND_SIGNER_CONFIG), tr("Send completed signer config to all other authorized signers")); @@ -3970,7 +3981,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty()) { - m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) || + m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) && command_line::is_arg_defaulted(vm, arg_restore_date))); if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date)) { @@ -4087,7 +4098,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_wallet->callback(this); - check_background_mining(password); + bool skip_check_backround_mining = !command_line::get_arg(vm, arg_command).empty(); + if (!skip_check_backround_mining) + check_background_mining(password); if (welcome) message_writer(console_color_yellow, true) << tr("If you are new to Monero, type \"welcome\" for a brief overview."); @@ -4237,7 +4250,9 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); if (!m_wallet) { @@ -4332,7 +4347,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); if (!m_wallet) { @@ -4378,7 +4395,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr //---------------------------------------------------------------------------------------------------- boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); m_wallet->callback(this); if (!m_wallet) @@ -4419,7 +4438,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const epee::wipeable_string &multisig_keys, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); if (!m_wallet) { @@ -5608,6 +5629,43 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector) +{ + // count the number of old outputs + std::string err; + uint64_t bc_height = get_daemon_blockchain_height(err); + if (!err.empty()) + return true; + + int max_n_old = 0; + for (const auto &ptx: ptx_vector) + { + int n_old = 0; + for (const auto i: ptx.selected_transfers) + { + const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); + uint64_t age = bc_height - td.m_block_height; + if (age > OLD_AGE_WARN_THRESHOLD) + ++n_old; + } + max_n_old = std::max(max_n_old, n_old); + } + if (max_n_old > 1) + { + std::stringstream prompt; + prompt << tr("Transaction spends more than one very old output. Privacy would be better if they were sent separately."); + prompt << ENDL << tr("Spend them now anyway?"); + std::string accepted = input_line(prompt.str(), true); + if (std::cin.eof()) + return false; + if (!command_line::is_yes(accepted)) + { + return false; + } + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms) { // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]" @@ -5909,6 +5967,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } } + if (!prompt_if_old(ptx_vector)) + { + fail_msg_writer() << tr("transaction cancelled."); + return false; + } + // if more than one tx necessary, prompt user to confirm if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) { @@ -6092,7 +6156,8 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_) { - return sweep_main(0, true, args_); + sweep_main(0, true, args_); + return true; } //---------------------------------------------------------------------------------------------------- @@ -6412,6 +6477,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st return true; } + if (!prompt_if_old(ptx_vector)) + { + fail_msg_writer() << tr("transaction cancelled."); + return false; + } + // give user total and fee, and prompt to confirm uint64_t total_fee = 0, total_sent = 0; for (size_t n = 0; n < ptx_vector.size(); ++n) @@ -6758,7 +6829,8 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { - return sweep_main(0, false, args_); + sweep_main(0, false, args_); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_below(const std::vector<std::string> &args_) @@ -6774,7 +6846,8 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) fail_msg_writer() << tr("invalid amount threshold"); return true; } - return sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) @@ -7747,7 +7820,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec uint64_t fee = pd.m_amount_in - pd.m_amount_out; std::vector<std::pair<std::string, uint64_t>> destinations; for (const auto &d: pd.m_dests) { - destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount}); + destinations.push_back({d.address(m_wallet->nettype(), pd.m_payment_id), d.amount}); } std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) @@ -7823,7 +7896,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec uint64_t fee = amount - pd.m_amount_out; std::vector<std::pair<std::string, uint64_t>> destinations; for (const auto &d: pd.m_dests) { - destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount}); + destinations.push_back({d.address(m_wallet->nettype(), pd.m_payment_id), d.amount}); } std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) @@ -8501,6 +8574,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: // address all // address <index_min> [<index_max>] // address label <index> <label text with white spaces allowed> + // address device [<index>] std::vector<std::string> local_args = args; tools::wallet2::transfer_container transfers; @@ -8537,6 +8611,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: label = tr("(Untitled address)"); m_wallet->add_subaddress(m_current_subaddress_account, label); print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1); + m_wallet->device_show_address(m_current_subaddress_account, m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1, boost::none); } else if (local_args.size() >= 2 && local_args[0] == "label") { @@ -8585,6 +8660,27 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: for (index = index_min; index <= index_max; ++index) print_address_sub(index); } + else if (local_args[0] == "device") + { + index = 0; + local_args.erase(local_args.begin()); + if (local_args.size() > 0) + { + if (!epee::string_tools::get_xtype_from_string(index, local_args[0])) + { + fail_msg_writer() << tr("failed to parse index: ") << local_args[0]; + return true; + } + if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account)) + { + fail_msg_writer() << tr("<index> is out of bounds"); + return true; + } + } + + print_address_sub(index); + m_wallet->device_show_address(m_current_subaddress_account, index, boost::none); + } else { PRINT_USAGE(USAGE_ADDRESS); @@ -8596,12 +8692,29 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: bool simple_wallet::print_integrated_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { crypto::hash8 payment_id; - if (args.size() > 1) + bool display_on_device = false; + std::vector<std::string> local_args = args; + + if (local_args.size() > 0 && local_args[0] == "device") + { + local_args.erase(local_args.begin()); + display_on_device = true; + } + + auto device_show_integrated = [this, display_on_device](crypto::hash8 payment_id) + { + if (display_on_device) + { + m_wallet->device_show_address(m_current_subaddress_account, 0, payment_id); + } + }; + + if (local_args.size() > 1) { PRINT_USAGE(USAGE_INTEGRATED_ADDRESS); return true; } - if (args.size() == 0) + if (local_args.size() == 0) { if (m_current_subaddress_account != 0) { @@ -8611,9 +8724,10 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg payment_id = crypto::rand<crypto::hash8>(); success_msg_writer() << tr("Random payment ID: ") << payment_id; success_msg_writer() << tr("Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype()); + device_show_integrated(payment_id); return true; } - if(tools::wallet2::parse_short_payment_id(args.back(), payment_id)) + if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id)) { if (m_current_subaddress_account != 0) { @@ -8621,16 +8735,18 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg return true; } success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype()); + device_show_integrated(payment_id); return true; } else { address_parse_info info; - if(get_account_address_from_str(info, m_wallet->nettype(), args.back())) + if(get_account_address_from_str(info, m_wallet->nettype(), local_args.back())) { if (info.has_payment_id) { success_msg_writer() << boost::format(tr("Integrated address: %s, payment ID: %s")) % get_account_address_as_str(m_wallet->nettype(), false, info.address) % epee::string_tools::pod_to_hex(info.payment_id); + device_show_integrated(info.payment_id); } else { @@ -8937,21 +9053,31 @@ bool simple_wallet::verify(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::export_key_images(const std::vector<std::string> &args) +bool simple_wallet::export_key_images(const std::vector<std::string> &args_) { if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if (args.size() != 1) + auto args = args_; + + if (m_wallet->watch_only()) { - PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES); + fail_msg_writer() << tr("wallet is watch-only and cannot export key images"); return true; } - if (m_wallet->watch_only()) + + bool all = false; + if (args.size() >= 2 && args[0] == "all") { - fail_msg_writer() << tr("wallet is watch-only and cannot export key images"); + all = true; + args.erase(args.begin()); + } + + if (args.size() != 1) + { + PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES); return true; } @@ -8963,7 +9089,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args) try { - if (!m_wallet->export_key_images(filename)) + if (!m_wallet->export_key_images(filename, all)) { fail_msg_writer() << tr("failed to save file ") << filename; return true; @@ -9088,13 +9214,22 @@ bool simple_wallet::hw_reconnect(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::export_outputs(const std::vector<std::string> &args) +bool simple_wallet::export_outputs(const std::vector<std::string> &args_) { if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); return true; } + auto args = args_; + + bool all = false; + if (args.size() >= 2 && args[0] == "all") + { + all = true; + args.erase(args.begin()); + } + if (args.size() != 1) { PRINT_USAGE(USAGE_EXPORT_OUTPUTS); @@ -9109,7 +9244,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args) try { - std::string data = m_wallet->export_outputs_to_str(); + std::string data = m_wallet->export_outputs_to_str(all); bool r = epee::file_io_utils::save_string_to_file(filename, data); if (!r) { @@ -9236,7 +9371,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) for (const auto &d: pd.m_dests) { if (!dests.empty()) dests += ", "; - dests += get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) + ": " + print_money(d.amount); + dests += d.address(m_wallet->nettype(), pd.m_payment_id) + ": " + print_money(d.amount); } std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 33b18612c..2de390666 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -109,6 +109,7 @@ namespace cryptonote bool spendkey(const std::vector<std::string> &args = std::vector<std::string>()); bool seed(const std::vector<std::string> &args = std::vector<std::string>()); bool encrypted_seed(const std::vector<std::string> &args = std::vector<std::string>()); + bool restore_height(const std::vector<std::string> &args = std::vector<std::string>()); /*! * \brief Sets seed language. @@ -261,6 +262,7 @@ namespace cryptonote void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; bool freeze_thaw(const std::vector<std::string>& args, bool freeze); + bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector); struct transfer_view { |