diff options
Diffstat (limited to 'src/simplewallet/simplewallet.cpp')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 264 |
1 files changed, 228 insertions, 36 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5631df655..c5a4f15a4 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -122,7 +122,7 @@ namespace const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""}; const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false}; const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false}; - const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Create non-deterministic view and spend keys"), false}; + const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false}; const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false}; 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}; const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; @@ -442,6 +442,21 @@ namespace fail_msg_writer() << tr("unexpected error: ") << e.what(); } } + + bool check_file_overwrite(const std::string &filename) + { + boost::system::error_code errcode; + if (boost::filesystem::exists(filename, errcode)) + { + if (boost::ends_with(filename, ".keys")) + { + fail_msg_writer() << boost::format(tr("File %s likely stores wallet private keys! Use a different file name.")) % filename; + return false; + } + return command_line::is_yes(input_line((boost::format(tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str())); + } + return true; + } } bool parse_priority(const std::string& arg, uint32_t& priority) @@ -874,6 +889,8 @@ bool simple_wallet::export_multisig(const std::vector<std::string> &args) return true; const std::string filename = args[0]; + if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) + return true; try { cryptonote::blobdata ciphertext = m_wallet->export_multisig(); @@ -1122,6 +1139,8 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args) if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } std::string filename = args[0]; + if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) + return true; try { tools::wallet2::multisig_tx_set txs; @@ -1384,7 +1403,7 @@ bool simple_wallet::set_unit(const std::vector<std::string> &args/* = std::vecto const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->set_default_decimal_point(decimal_point); + cryptonote::set_default_decimal_point(decimal_point); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; @@ -1470,6 +1489,19 @@ bool simple_wallet::set_confirm_backlog_threshold(const std::vector<std::string> return true; } +bool simple_wallet::set_confirm_export_overwrite(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->confirm_export_overwrite(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + bool simple_wallet::set_refresh_from_block_height(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -1487,6 +1519,19 @@ bool simple_wallet::set_refresh_from_block_height(const std::vector<std::string> return true; } +bool simple_wallet::set_auto_low_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->auto_low_priority(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { if(args.empty()) @@ -1659,7 +1704,9 @@ simple_wallet::simple_wallet() "confirm-backlog-threshold [n]\n " " Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks.\n " "refresh-from-block-height [n]\n " - " Set the height before which to ignore blocks.")); + " Set the height before which to ignore blocks.\n " + "auto-low-priority <1|0>\n " + " Whether to automatically use the low priority fee level when it's safe to do so.")); m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display the encrypted Electrum-style mnemonic seed.")); @@ -1674,9 +1721,9 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::check_tx_key, this, _1), tr("check_tx_key <txid> <txkey> <address>"), tr("Check the amount going to <address> in <txid>.")); - m_cmd_binder.set_handler("get_tx_proof_out", + m_cmd_binder.set_handler("get_tx_proof", boost::bind(&simple_wallet::get_tx_proof, this, _1), - tr("get_tx_proof_out <txid> <address> [<message>]"), + tr("get_tx_proof <txid> <address> [<message>]"), 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.")); m_cmd_binder.set_handler("check_tx_proof", boost::bind(&simple_wallet::check_tx_proof, this, _1), @@ -1690,6 +1737,16 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::check_spend_proof, this, _1), tr("check_spend_proof <txid> <signature_file> [<message>]"), tr("Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>.")); + m_cmd_binder.set_handler("get_reserve_proof", + boost::bind(&simple_wallet::get_reserve_proof, this, _1), + tr("get_reserve_proof (all|<amount>) [<message>]"), + tr("Generate a signature proving that you own at least this much, optionally with a challenge string <message>.\n" + "If 'all' is specified, you prove the entire sum of all of your existing accounts' balances.\n" + "Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account.")); + m_cmd_binder.set_handler("check_reserve_proof", + boost::bind(&simple_wallet::check_reserve_proof, this, _1), + tr("check_reserve_proof <address> <signature_file> [<message>]"), + tr("Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>.")); m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), tr("show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"), @@ -1770,11 +1827,11 @@ simple_wallet::simple_wallet() tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets")); m_cmd_binder.set_handler("export_multisig_info", boost::bind(&simple_wallet::export_multisig, this, _1), - tr("export_multisig <filename>"), + tr("export_multisig_info <filename>"), tr("Export multisig info for other participants")); m_cmd_binder.set_handler("import_multisig_info", boost::bind(&simple_wallet::import_multisig, this, _1), - tr("import_multisig <filename> [<filename>...]"), + tr("import_multisig_info <filename> [<filename>...]"), tr("Import multisig info from other participants")); m_cmd_binder.set_handler("sign_multisig", boost::bind(&simple_wallet::sign_multisig, this, _1), @@ -1786,7 +1843,7 @@ simple_wallet::simple_wallet() tr("Submit a signed multisig transaction from a file")); m_cmd_binder.set_handler("export_raw_multisig_tx", boost::bind(&simple_wallet::export_raw_multisig, this, _1), - tr("export_raw_multisig <filename>"), + tr("export_raw_multisig_tx <filename>"), tr("Export a signed multisig transaction to a file")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), @@ -1808,13 +1865,15 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "priority = " << m_wallet->get_default_priority(); success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id(); success_msg_writer() << "ask-password = " << m_wallet->ask_password(); - success_msg_writer() << "unit = " << cryptonote::get_unit(m_wallet->get_default_decimal_point()); + success_msg_writer() << "unit = " << cryptonote::get_unit(cryptonote::get_default_decimal_point()); success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count(); success_msg_writer() << "min-outputs-value = " << cryptonote::print_money(m_wallet->get_min_output_value()); success_msg_writer() << "merge-destinations = " << m_wallet->merge_destinations(); success_msg_writer() << "confirm-backlog = " << m_wallet->confirm_backlog(); success_msg_writer() << "confirm-backlog-threshold = " << m_wallet->get_confirm_backlog_threshold(); + success_msg_writer() << "confirm-export-overwrite = " << m_wallet->confirm_export_overwrite(); success_msg_writer() << "refresh-from-block-height = " << m_wallet->get_refresh_from_block_height(); + success_msg_writer() << "auto-low-priority = " << m_wallet->auto_low_priority(); return true; } else @@ -1862,7 +1921,9 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("confirm-backlog", set_confirm_backlog, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("confirm-backlog-threshold", set_confirm_backlog_threshold, tr("unsigned integer")); + CHECK_SIMPLE_VARIABLE("confirm-export-overwrite", set_confirm_export_overwrite, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-from-block-height", set_refresh_from_block_height, tr("block height")); + CHECK_SIMPLE_VARIABLE("auto-low-priority", set_auto_low_priority, tr("0 or 1")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -3307,6 +3368,13 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args { if (!parse_subaddress_indices(local_args[0], subaddr_indices)) return true; + local_args.erase(local_args.begin()); + } + + if (local_args.size() > 0) + { + fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]"); + return true; } tools::wallet2::transfer_container transfers; @@ -3616,6 +3684,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (local_args.size() > 0 && parse_priority(local_args[0], priority)) local_args.erase(local_args.begin()); + priority = m_wallet->adjust_priority(priority); + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; @@ -3803,7 +3873,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { std::stringstream prompt; double worst_fee_per_byte = std::numeric_limits<double>::max(); - uint64_t size = 0, fee = 0; for (size_t n = 0; n < ptx_vector.size(); ++n) { const uint64_t blob_size = cryptonote::tx_to_blob(ptx_vector[n].tx).size(); @@ -3811,13 +3880,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (fee_per_byte < worst_fee_per_byte) { worst_fee_per_byte = fee_per_byte; - fee = ptx_vector[n].fee; } - size += blob_size; } try { - std::vector<std::pair<uint64_t, uint64_t>> nblocks = m_wallet->estimate_backlog(size, size, {fee}); + std::vector<std::pair<uint64_t, uint64_t>> nblocks = m_wallet->estimate_backlog({std::make_pair(worst_fee_per_byte, worst_fee_per_byte)}); if (nblocks.size() != 1) { prompt << "Internal error checking for backlog. " << tr("Is this okay anyway? (Y/Yes/N/No): "); @@ -3825,7 +3892,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri else { if (nblocks[0].first > m_wallet->get_confirm_backlog_threshold()) - prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No)")) % nblocks[0].first).str(); + prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): ")) % nblocks[0].first).str(); } } catch (const std::exception &e) @@ -4096,6 +4163,8 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a if (local_args.size() > 0 && parse_priority(local_args[0], priority)) local_args.erase(local_args.begin()); + priority = m_wallet->adjust_priority(priority); + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; @@ -4243,7 +4312,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a print_money(total_fee); } else { - prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No)")) % + prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) % print_money(total_sent) % print_money(total_fee); } @@ -4308,17 +4377,11 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) std::vector<std::string> local_args = args_; - int priority = 0; - if(local_args.size() > 0) { - auto priority_pos = std::find( - allowed_priority_strings.begin(), - allowed_priority_strings.end(), - local_args[0]); - if(priority_pos != allowed_priority_strings.end()) { - local_args.erase(local_args.begin()); - priority = std::distance(allowed_priority_strings.begin(), priority_pos); - } - } + uint32_t priority = 0; + if (local_args.size() > 0 && parse_priority(local_args[0], priority)) + local_args.erase(local_args.begin()); + + priority = m_wallet->adjust_priority(priority); size_t fake_outs_count = 0; if(local_args.size() > 0) { @@ -4448,7 +4511,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) std::ostringstream prompt; if (!print_ring_members(ptx_vector, prompt)) return true; - prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No)")) % + prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) % print_money(total_sent) % print_money(total_fee); std::string accepted = input_line(prompt.str()); @@ -4528,14 +4591,18 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) { + if(m_wallet->testnet()) + { + fail_msg_writer() << tr("donations are not enabled on the testnet"); + return true; + } + std::vector<std::string> local_args = args_; if(local_args.empty() || local_args.size() > 5) { fail_msg_writer() << tr("usage: donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>]"); return true; } - // Hardcode Monero's donation address (see #1447) - const std::string address_str = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; std::string amount_str; std::string payment_id_str; // get payment id and pop @@ -4551,11 +4618,11 @@ bool simple_wallet::donate(const std::vector<std::string> &args_) amount_str = local_args.back(); local_args.pop_back(); // push back address, amount, payment id - local_args.push_back(address_str); + local_args.push_back(MONERO_DONATION_ADDR); local_args.push_back(amount_str); if (!payment_id_str.empty()) local_args.push_back(payment_id_str); - message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org/44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A)."; + message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org/"<< MONERO_DONATION_ADDR <<")."; transfer_new(local_args); return true; } @@ -4656,12 +4723,24 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, payment_id_string = "no payment ID"; std::string dest_string; + size_t n_dummy_outputs = 0; for (auto i = dests.begin(); i != dests.end(); ) { - dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second.second) % i->second.first).str(); + if (i->second.second > 0) + { + if (!dest_string.empty()) + dest_string += ", "; + dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second.second) % i->second.first).str(); + } + else + ++n_dummy_outputs; ++i; - if (i != dests.end()) + } + if (n_dummy_outputs > 0) + { + if (!dest_string.empty()) dest_string += ", "; + dest_string += std::to_string(n_dummy_outputs) + tr(" dummy output(s)"); } if (dest_string.empty()) dest_string = tr("with no destinations"); @@ -4825,7 +4904,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args) { if (args.size() != 2 && args.size() != 3) { - fail_msg_writer() << tr("usage: get_tx_proof_out <txid> <address> [<message>]"); + fail_msg_writer() << tr("usage: get_tx_proof <txid> <address> [<message>]"); return true; } @@ -5112,6 +5191,110 @@ bool simple_wallet::check_spend_proof(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args) +{ + if(args.size() != 1 && args.size() != 2) { + fail_msg_writer() << tr("usage: get_reserve_proof (all|<amount>) [<message>]"); + return true; + } + + if (m_wallet->watch_only() || m_wallet->multisig()) + { + fail_msg_writer() << tr("The reserve proof can be generated only by a full wallet"); + return true; + } + + boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve; + if (args[0] != "all") + { + account_minreserve = std::pair<uint32_t, uint64_t>(); + account_minreserve->first = m_current_subaddress_account; + if (!cryptonote::parse_amount(account_minreserve->second, args[0])) + { + fail_msg_writer() << tr("amount is wrong: ") << args[0]; + return true; + } + } + + if (!try_connect_to_daemon()) + { + fail_msg_writer() << tr("failed to connect to the daemon"); + return true; + } + + if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + LOCK_IDLE_SCOPE(); + + try + { + const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] : ""); + const std::string filename = "monero_reserve_proof"; + if (epee::file_io_utils::save_string_to_file(filename, sig_str)) + success_msg_writer() << tr("signature file saved to: ") << filename; + else + fail_msg_writer() << tr("failed to save signature file"); + } + catch (const std::exception &e) + { + fail_msg_writer() << e.what(); + } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::check_reserve_proof(const std::vector<std::string> &args) +{ + if(args.size() != 2 && args.size() != 3) { + fail_msg_writer() << tr("usage: check_reserve_proof <address> <signature_file> [<message>]"); + return true; + } + + if (!try_connect_to_daemon()) + { + fail_msg_writer() << tr("failed to connect to the daemon"); + return true; + } + + cryptonote::address_parse_info info; + if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), args[0], oa_prompter)) + { + fail_msg_writer() << tr("failed to parse address"); + return true; + } + if (info.is_subaddress) + { + fail_msg_writer() << tr("Address must not be a subaddress"); + return true; + } + + std::string sig_str; + if (!epee::file_io_utils::load_file_to_string(args[1], sig_str)) + { + fail_msg_writer() << tr("failed to load signature file"); + return true; + } + + LOCK_IDLE_SCOPE(); + + try + { + uint64_t total, spent; + if (m_wallet->check_reserve_proof(info.address, args.size() == 3 ? args[2] : "", sig_str, total, spent)) + { + success_msg_writer() << boost::format(tr("Good signature -- total: %s, spent: %s, unspent: %s")) % print_money(total) % print_money(spent) % print_money(total - spent); + } + else + { + fail_msg_writer() << tr("Bad signature"); + } + } + catch (const std::exception& e) + { + fail_msg_writer() << e.what(); + } + return true; +} +//---------------------------------------------------------------------------------------------------- static std::string get_human_readable_timestamp(uint64_t ts) { char buffer[64]; @@ -6192,9 +6375,12 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args) } if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } std::string filename = args[0]; + if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) + return true; try { + LOCK_IDLE_SCOPE(); if (!m_wallet->export_key_images(filename)) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -6227,6 +6413,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args) } std::string filename = args[0]; + LOCK_IDLE_SCOPE(); try { uint64_t spent = 0, unspent = 0; @@ -6257,7 +6444,10 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args) } if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } std::string filename = args[0]; + if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) + return true; + LOCK_IDLE_SCOPE(); try { std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs(); @@ -6356,6 +6546,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args) boost::archive::binary_iarchive ar(iss); ar >> outputs; } + LOCK_IDLE_SCOPE(); size_t n_outputs = m_wallet->import_outputs(outputs); success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported"; } @@ -6607,7 +6798,8 @@ int main(int argc, char* argv[]) std::vector<std::string> command = command_line::get_arg(*vm, arg_command); if (!command.empty()) { - w.process_command(command); + if (!w.process_command(command)) + fail_msg_writer() << tr("Unknown command: ") << command.front(); w.stop(); w.deinit(); } |