diff options
Diffstat (limited to 'src/simplewallet')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 554 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 14 |
2 files changed, 543 insertions, 25 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3668df7b9..d573f317b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -62,6 +62,7 @@ #include "ringct/rctSigs.h" #include "multisig/multisig.h" #include "wallet/wallet_args.h" +#include "version.h" #include <stdexcept> #ifdef WIN32 @@ -134,6 +135,8 @@ namespace const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false}; const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; + const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""}; + const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false}; const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; @@ -375,8 +378,28 @@ namespace return true; } - void handle_transfer_exception(const std::exception_ptr &e) + boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str) { + auto pos = str.find(":"); + bool r = pos != std::string::npos; + uint32_t major; + r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos)); + uint32_t minor; + r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1)); + if (r) + { + return std::make_pair(major, minor); + } + else + { + fail_msg_writer() << tr("invalid format for subaddress lookahead; must be <major>:<minor>"); + return {}; + } + } + + void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon) + { + bool warn_of_possible_attack = !trusted_daemon; try { std::rethrow_exception(e); @@ -404,6 +427,7 @@ namespace print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); + warn_of_possible_attack = false; } catch (const tools::error::not_enough_money& e) { @@ -411,6 +435,7 @@ namespace print_money(e.available()) % print_money(e.tx_amount())); fail_msg_writer() << tr("Not enough money in unlocked balance"); + warn_of_possible_attack = false; } catch (const tools::error::tx_not_possible& e) { @@ -420,6 +445,7 @@ namespace print_money(e.tx_amount()) % print_money(e.fee())); fail_msg_writer() << 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 money than the unlocked balance, or not leaving enough for fees"); + warn_of_possible_attack = false; } catch (const tools::error::not_enough_outs_to_mix& e) { @@ -434,6 +460,7 @@ namespace catch (const tools::error::tx_not_constructed&) { fail_msg_writer() << tr("transaction was not constructed"); + warn_of_possible_attack = false; } catch (const tools::error::tx_rejected& e) { @@ -445,14 +472,17 @@ namespace catch (const tools::error::tx_sum_overflow& e) { fail_msg_writer() << e.what(); + warn_of_possible_attack = false; } catch (const tools::error::zero_destination&) { fail_msg_writer() << tr("one of destinations is zero"); + warn_of_possible_attack = false; } catch (const tools::error::tx_too_big& e) { fail_msg_writer() << tr("failed to find a suitable way to split transactions"); + warn_of_possible_attack = false; } catch (const tools::error::transfer_error& e) { @@ -463,6 +493,7 @@ namespace { LOG_ERROR("Multisig error: " << e.to_string()); fail_msg_writer() << tr("Multisig error: ") << e.what(); + warn_of_possible_attack = false; } catch (const tools::error::wallet_internal_error& e) { @@ -474,6 +505,9 @@ namespace LOG_ERROR("unexpected error: " << e.what()); fail_msg_writer() << tr("unexpected error: ") << e.what(); } + + if (warn_of_possible_attack) + fail_msg_writer() << 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 tranasction immediately. Alternatively, connect to another node so the original node cannot correlate information."); } bool check_file_overwrite(const std::string &filename) @@ -725,7 +759,7 @@ bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std: } const uint64_t per_kb_fee = m_wallet->get_per_kb_fee(); const uint64_t typical_size_kb = 13; - message_writer() << (boost::format(tr("Current fee is %s monero per kB")) % print_money(per_kb_fee)).str(); + message_writer() << (boost::format(tr("Current fee is %s %s per kB")) % print_money(per_kb_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point())).str(); std::vector<uint64_t> fees; for (uint32_t priority = 1; priority <= 4; ++priority) @@ -1195,7 +1229,7 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args) } catch (const std::exception &e) { - handle_transfer_exception(std::current_exception()); + handle_transfer_exception(std::current_exception(), m_trusted_daemon); } catch (...) { @@ -1282,6 +1316,281 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args) return true; } +bool simple_wallet::print_ring(const std::vector<std::string> &args) +{ + crypto::key_image key_image; + crypto::hash txid; + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: print_ring <key_image|txid>"); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], key_image)) + { + fail_msg_writer() << tr("Invalid key image"); + return true; + } + // this one will always work, they're all 32 byte hex + if (!epee::string_tools::hex_to_pod(args[0], txid)) + { + fail_msg_writer() << tr("Invalid txid"); + return true; + } + + std::vector<uint64_t> ring; + std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings; + try + { + if (m_wallet->get_ring(key_image, ring)) + rings.push_back({key_image, ring}); + else if (!m_wallet->get_rings(txid, rings)) + { + fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + return true; + } + + for (const auto &ring: rings) + { + std::stringstream str; + for (const auto &x: ring.second) + str << x<< " "; + // do NOT translate this "absolute" below, the lin can be used as input to set_ring + success_msg_writer() << epee::string_tools::pod_to_hex(ring.first) << " absolute " << str.str(); + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to get key image ring: ") << e.what(); + } + + return true; +} + +bool simple_wallet::set_ring(const std::vector<std::string> &args) +{ + crypto::key_image key_image; + if (args.size() < 3) + { + fail_msg_writer() << tr("usage: set_ring <key_image> absolute|relative <index> [<index>...]"); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], key_image)) + { + fail_msg_writer() << tr("Invalid key image"); + return true; + } + + bool relative; + if (args[1] == "absolute") + { + relative = false; + } + else if (args[1] == "relative") + { + relative = true; + } + else + { + fail_msg_writer() << tr("Missing absolute or relative keyword"); + return true; + } + + std::vector<uint64_t> ring; + for (size_t n = 2; n < args.size(); ++n) + { + ring.resize(ring.size() + 1); + if (!string_tools::get_xtype_from_string(ring.back(), args[n])) + { + fail_msg_writer() << tr("invalid index: must be a strictly positive unsigned integer"); + return true; + } + if (relative) + { + if (ring.size() > 1 && !ring.back()) + { + fail_msg_writer() << tr("invalid index: must be a strictly positive unsigned integer"); + return true; + } + uint64_t sum = 0; + for (uint64_t out: ring) + { + if (out > std::numeric_limits<uint64_t>::max() - sum) + { + fail_msg_writer() << tr("invalid index: indices wrap"); + return true; + } + sum += out; + } + } + else + { + if (ring.size() > 1 && ring[ring.size() - 2] >= ring[ring.size() - 1]) + { + fail_msg_writer() << tr("invalid index: indices should be in strictly ascending order"); + return true; + } + } + } + if (!m_wallet->set_ring(key_image, ring, relative)) + { + fail_msg_writer() << tr("failed to set ring"); + return true; + } + + return true; +} + +bool simple_wallet::blackball(const std::vector<std::string> &args) +{ + crypto::public_key output; + if (args.size() == 0) + { + fail_msg_writer() << tr("usage: blackball <output_public_key> | <filename> [add]"); + return true; + } + + try + { + if (epee::string_tools::hex_to_pod(args[0], output)) + { + m_wallet->blackball_output(output); + } + else if (epee::file_io_utils::is_file_exist(args[0])) + { + std::vector<crypto::public_key> outputs; + char str[65]; + + std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r")); + if (f) + { + while (!feof(f.get())) + { + if (!fgets(str, sizeof(str), f.get())) + break; + const size_t len = strlen(str); + if (len > 0 && str[len - 1] == '\n') + str[len - 1] = 0; + if (!str[0]) + continue; + outputs.push_back(crypto::public_key()); + if (!epee::string_tools::hex_to_pod(str, outputs.back())) + { + fail_msg_writer() << tr("Invalid public key: ") << str; + return true; + } + } + f.reset(); + bool add = false; + if (args.size() > 1) + { + if (args[1] != "add") + { + fail_msg_writer() << tr("Bad argument: ") + args[1] + ": " + tr("should be \"add\""); + return true; + } + add = true; + } + m_wallet->set_blackballed_outputs(outputs, add); + } + else + { + fail_msg_writer() << tr("Failed to open file"); + return true; + } + } + else + { + fail_msg_writer() << tr("Invalid public key, and file doesn't exist"); + return true; + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to blackball output: ") << e.what(); + } + + return true; +} + +bool simple_wallet::unblackball(const std::vector<std::string> &args) +{ + crypto::public_key output; + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: unblackball <output_public_key>"); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], output)) + { + fail_msg_writer() << tr("Invalid public key"); + return true; + } + + try + { + m_wallet->unblackball_output(output); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); + } + + return true; +} + +bool simple_wallet::blackballed(const std::vector<std::string> &args) +{ + crypto::public_key output; + if (args.size() != 1) + { + fail_msg_writer() << tr("usage: blackballed <output_public_key>"); + return true; + } + + if (!epee::string_tools::hex_to_pod(args[0], output)) + { + fail_msg_writer() << tr("Invalid public key"); + return true; + } + + try + { + if (m_wallet->is_output_blackballed(output)) + message_writer() << tr("Blackballed: ") << output; + else + message_writer() << tr("not blackballed: ") << output; + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); + } + + return true; +} + +bool simple_wallet::save_known_rings(const std::vector<std::string> &args) +{ + try + { + LOCK_IDLE_SCOPE(); + m_wallet->find_and_save_rings(); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to save known rings: ") << e.what(); + } + return true; +} + +bool simple_wallet::version(const std::vector<std::string> &args) +{ + message_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + return true; +} + bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -1348,6 +1657,9 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/* return true; } + if (ring_size != 0 && ring_size != DEFAULT_MIX+1) + message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); + const auto pwd_container = get_and_verify_password(); if (pwd_container) { @@ -1626,6 +1938,64 @@ bool simple_wallet::set_auto_low_priority(const std::vector<std::string> &args/* return true; } +bool simple_wallet::set_segregate_pre_fork_outputs(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->segregate_pre_fork_outputs(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + +bool simple_wallet::set_key_reuse_mitigation2(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->key_reuse_mitigation2(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + +bool simple_wallet::set_subaddress_lookahead(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + auto lookahead = parse_subaddress_lookahead(args[1]); + if (lookahead) + { + m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + } + return true; +} + +bool simple_wallet::set_segregation_height(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + uint64_t height; + if (!epee::string_tools::get_xtype_from_string(height, args[1])) + { + fail_msg_writer() << tr("Invalid height"); + return true; + } + m_wallet->segregation_height(height); + 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()) @@ -1711,8 +2081,8 @@ simple_wallet::simple_wallet() tr("Donate <amount> to the development team (donate.getmonero.org).")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), - tr("sign_transfer <file>"), - tr("Sign a transaction from a <file>.")); + tr("sign_transfer [export]"), + tr("Sign a transaction from a file.")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file.")); @@ -1739,7 +2109,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), tr("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"), - tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the walllet 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.")); + 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.")); m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::print_integrated_address, this, _1), tr("integrated_address [<payment_id> | <address>]"), @@ -1800,7 +2170,16 @@ simple_wallet::simple_wallet() "refresh-from-block-height [n]\n " " 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.")); + " Whether to automatically use the low priority fee level when it's safe to do so.\n " + "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" + "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 " + "segregation-height <n>\n " + " Set to the height of a key reusing fork you want to use, 0 to use default.")); m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display the encrypted Electrum-style mnemonic seed.")); @@ -1939,6 +2318,34 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::export_raw_multisig, this, _1), tr("export_raw_multisig_tx <filename>"), tr("Export a signed multisig transaction to a file")); + m_cmd_binder.set_handler("print_ring", + boost::bind(&simple_wallet::print_ring, this, _1), + tr("print_ring <key_image> | <txid>"), + tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)")); + m_cmd_binder.set_handler("set_ring", + boost::bind(&simple_wallet::set_ring, this, _1), + tr("set_ring <key_image> absolute|relative <index> [<index>...]"), + tr("Set the ring used for a given key image, so it can be reused in a fork")); + m_cmd_binder.set_handler("save_known_rings", + boost::bind(&simple_wallet::save_known_rings, this, _1), + tr("save_known_rings"), + tr("Save known rings to the shared rings database")); + m_cmd_binder.set_handler("blackball", + boost::bind(&simple_wallet::blackball, this, _1), + tr("blackball <output public key> | <filename> [add]"), + tr("Blackball output(s) so they never get selected as fake outputs in a ring")); + m_cmd_binder.set_handler("unblackball", + boost::bind(&simple_wallet::unblackball, this, _1), + tr("unblackball <output public key>"), + tr("Unblackballs an output so it may get selected as a fake output in a ring")); + m_cmd_binder.set_handler("blackballed", + boost::bind(&simple_wallet::blackballed, this, _1), + tr("blackballed <output public key>"), + tr("Checks whether an output is blackballed")); + m_cmd_binder.set_handler("version", + boost::bind(&simple_wallet::version, this, _1), + tr("version"), + tr("Returns version information")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("help [<command>]"), @@ -1949,7 +2356,10 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) { if (args.empty()) { - success_msg_writer() << "seed = " << m_wallet->get_seed_language(); + std::string seed_language = m_wallet->get_seed_language(); + if (m_use_english_language_names) + seed_language = crypto::ElectrumWords::get_english_name_for(seed_language); + success_msg_writer() << "seed = " << seed_language; success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers(); success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members(); success_msg_writer() << "store-tx-info = " << m_wallet->store_tx_info(); @@ -1968,6 +2378,11 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) 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(); + success_msg_writer() << "segregate-pre-fork-outputs = " << m_wallet->segregate_pre_fork_outputs(); + success_msg_writer() << "key-reuse-mitigation2 = " << m_wallet->key_reuse_mitigation2(); + const std::pair<size_t, size_t> lookahead = m_wallet->get_subaddress_lookahead(); + success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second; + success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); return true; } else @@ -2018,6 +2433,10 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) 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")); + CHECK_SIMPLE_VARIABLE("segregate-pre-fork-outputs", set_segregate_pre_fork_outputs, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("key-reuse-mitigation2", set_key_reuse_mitigation2, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>")); + CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -2173,6 +2592,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!m_generate_new.empty() || m_restoring) { + if (!m_subaddress_lookahead.empty() && !parse_subaddress_lookahead(m_subaddress_lookahead)) + return false; + std::string old_language; // check for recover flag. if present, require electrum word list (only recovery option for now). if (m_restore_deterministic_wallet || m_restore_multisig_wallet) @@ -2566,6 +2988,22 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) // create wallet bool r = new_wallet(vm, "Ledger"); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + // if no block_height is specified, assume its a new account and start it "now" + if(m_wallet->get_refresh_from_block_height() == 0) { + { + tools::scoped_message_writer wrt = tools::msg_writer(); + wrt << tr("No restore height is specified."); + wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height."); + wrt << tr("Use --restore-height if you want to restore an already setup account from a specific height"); + } + std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): ")); + if (std::cin.eof() || !command_line::is_yes(confirm)) + CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted")); + + m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height()-1); + m_wallet->explicit_refresh_from_block_height(true); + m_restore_height = m_wallet->get_refresh_from_block_height(); + } } else { @@ -2581,6 +3019,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } + + 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)); + } if (!m_wallet->explicit_refresh_from_block_height() && m_restoring) { uint32_t version; @@ -2660,6 +3103,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) else { assert(!m_wallet_file.empty()); + if (!m_subaddress_lookahead.empty()) + { + fail_msg_writer() << tr("can't specify --subaddress-lookahead and --wallet-file at the same time"); + return false; + } bool r = open_wallet(vm); CHECK_AND_ASSERT_MES(r, false, tr("failed to open account")); } @@ -2683,7 +3131,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!m_trusted_daemon) message_writer() << (boost::format(tr("Warning: using an untrusted daemon at %s, privacy will be lessened")) % m_wallet->get_daemon_address()).str(); - m_http_client.set_server(m_wallet->get_daemon_address(), m_wallet->get_daemon_login()); + if (m_wallet->get_ring_database().empty()) + fail_msg_writer() << tr("Failed to initialize ring database: privacy enhancing features will be inactive"); + m_wallet->callback(this); return true; @@ -2716,6 +3166,8 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version); m_restore_height = command_line::get_arg(vm, arg_restore_height); m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay); + m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead); + m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names); m_restoring = !m_generate_from_view_key.empty() || !m_generate_from_spend_key.empty() || !m_generate_from_keys.empty() || @@ -2762,8 +3214,9 @@ std::string simple_wallet::get_mnemonic_language() std::vector<std::string> language_list; std::string language_choice; int language_number = -1; - crypto::ElectrumWords::get_language_list(language_list); + crypto::ElectrumWords::get_language_list(language_list, m_use_english_language_names); std::cout << tr("List of available languages for your wallet's seed:") << std::endl; + std::cout << tr("If your display freezes, exit blind with ^C, then run again with --use-english-language-names") << std::endl; int ii; std::vector<std::string>::iterator it; for (it = language_list.begin(), ii = 0; it != language_list.end(); it++, ii++) @@ -2816,6 +3269,13 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, return false; } + if (!m_subaddress_lookahead.empty()) + { + auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead); + assert(lookahead); + m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second); + } + bool was_deprecated_wallet = m_restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) || crypto::ElectrumWords::get_is_old_style_seed(m_electrum_seed)); @@ -2899,6 +3359,14 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, { return false; } + + if (!m_subaddress_lookahead.empty()) + { + auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead); + assert(lookahead); + m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second); + } + if (m_restore_height) m_wallet->set_refresh_from_block_height(m_restore_height); @@ -2936,13 +3404,21 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, { return false; } + + if (!m_subaddress_lookahead.empty()) + { + auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead); + assert(lookahead); + m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second); + } + if (m_restore_height) m_wallet->set_refresh_from_block_height(m_restore_height); try { m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name); - message_writer(console_color_white, true) << tr("Generated new on device wallet: ") + message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } catch (const std::exception& e) @@ -2964,6 +3440,13 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, return false; } + if (!m_subaddress_lookahead.empty()) + { + auto lookahead = parse_subaddress_lookahead(m_subaddress_lookahead); + assert(lookahead); + m_wallet->set_subaddress_lookahead(lookahead->first, lookahead->second); + } + std::string mnemonic_language = old_language; std::vector<std::string> language_list; @@ -3216,7 +3699,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) } COMMAND_RPC_START_MINING::response res; - bool r = net_utils::invoke_http_json("/start_mining", req, res, m_http_client); + bool r = m_wallet->invoke_http_json("/start_mining", req, res); std::string err = interpret_rpc_response(r, res.status); if (err.empty()) success_msg_writer() << tr("Mining started in daemon"); @@ -3238,7 +3721,7 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args) COMMAND_RPC_STOP_MINING::request req; COMMAND_RPC_STOP_MINING::response res; - bool r = net_utils::invoke_http_json("/stop_mining", req, res, m_http_client); + bool r = m_wallet->invoke_http_json("/stop_mining", req, res); std::string err = interpret_rpc_response(r, res.status); if (err.empty()) success_msg_writer() << tr("Mining stopped in daemon"); @@ -3295,7 +3778,7 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args) } COMMAND_RPC_SAVE_BC::request req; COMMAND_RPC_SAVE_BC::response res; - bool r = net_utils::invoke_http_json("/save_bc", req, res, m_http_client); + bool r = m_wallet->invoke_http_json("/save_bc", req, res); std::string err = interpret_rpc_response(r, res.status); if (err.empty()) success_msg_writer() << tr("Blockchain saved"); @@ -3641,7 +4124,7 @@ uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) COMMAND_RPC_GET_HEIGHT::request req; COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>(); - bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client); + bool r = m_wallet->invoke_http_json("/getheight", req, res); err = interpret_rpc_response(r, res.status); return res.height; } @@ -3763,7 +4246,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending req.outputs[j].index = absolute_offsets[j]; } COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); - bool r = net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client); + bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res); err = interpret_rpc_response(r, res.status); if (!err.empty()) { @@ -4131,6 +4614,23 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (!print_ring_members(ptx_vector, prompt)) return true; } + bool default_ring_size = true; + for (const auto &ptx: ptx_vector) + { + for (const auto &vin: ptx.tx.vin) + { + if (vin.type() == typeid(txin_to_key)) + { + const txin_to_key& in_to_key = boost::get<txin_to_key>(vin); + if (in_to_key.key_offsets.size() != DEFAULT_MIX + 1) + default_ring_size = false; + } + } + } + if (m_wallet->confirm_non_default_ring_size() && !default_ring_size) + { + prompt << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); + } prompt << ENDL << tr("Is this okay? (Y/Yes/N/No): "); std::string accepted = input_line(prompt.str()); @@ -4176,7 +4676,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } catch (const std::exception &e) { - handle_transfer_exception(std::current_exception()); + handle_transfer_exception(std::current_exception(), m_trusted_daemon); } catch (...) { @@ -4284,7 +4784,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) } catch (const std::exception &e) { - handle_transfer_exception(std::current_exception()); + handle_transfer_exception(std::current_exception(), m_trusted_daemon); } catch (...) { @@ -4517,7 +5017,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a } catch (const std::exception& e) { - handle_transfer_exception(std::current_exception()); + handle_transfer_exception(std::current_exception(), m_trusted_daemon); } catch (...) { @@ -4716,7 +5216,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) } catch (const std::exception& e) { - handle_transfer_exception(std::current_exception()); + handle_transfer_exception(std::current_exception(), m_trusted_daemon); } catch (...) { @@ -5021,7 +5521,7 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_) } catch (const std::exception& e) { - handle_transfer_exception(std::current_exception()); + handle_transfer_exception(std::current_exception(), m_trusted_daemon); } catch (...) { @@ -6464,9 +6964,13 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args) { bool ready; uint32_t threshold, total; - + std::string description = m_wallet->get_description(); + if (description.empty()) + { + description = "<Not set>"; + } message_writer() << tr("Filename: ") << m_wallet->get_wallet_file(); - message_writer() << tr("Description: ") << m_wallet->get_description(); + message_writer() << tr("Description: ") << description; message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); std::string type; if (m_wallet->watch_only()) @@ -6995,6 +7499,8 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_restore_height); command_line::add_arg(desc_params, arg_do_not_relay); command_line::add_arg(desc_params, arg_create_address_file); + command_line::add_arg(desc_params, arg_subaddress_lookahead); + command_line::add_arg(desc_params, arg_use_english_language_names); po::positional_options_description positional_options; positional_options.add(arg_command.name, -1); @@ -7002,7 +7508,7 @@ int main(int argc, char* argv[]) const auto vm = wallet_args::main( argc, argv, "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]", - sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly."), + sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly.\nWARNING: Do not reuse your Monero keys on an another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), desc_params, positional_options, [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 4c7818bf1..39a91c5f5 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -133,6 +133,10 @@ namespace cryptonote bool set_confirm_export_overwrite(const std::vector<std::string> &args = std::vector<std::string>()); bool set_refresh_from_block_height(const std::vector<std::string> &args = std::vector<std::string>()); bool set_auto_low_priority(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_segregate_pre_fork_outputs(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_key_reuse_mitigation2(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); @@ -208,6 +212,13 @@ namespace cryptonote bool sign_multisig(const std::vector<std::string>& args); bool submit_multisig(const std::vector<std::string>& args); bool export_raw_multisig(const std::vector<std::string>& args); + bool print_ring(const std::vector<std::string>& args); + bool set_ring(const std::vector<std::string>& args); + bool save_known_rings(const std::vector<std::string>& args); + bool blackball(const std::vector<std::string>& args); + bool unblackball(const std::vector<std::string>& args); + bool blackballed(const std::vector<std::string>& args); + bool version(const std::vector<std::string>& args); uint64_t get_daemon_blockchain_height(std::string& err); bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr); @@ -312,6 +323,7 @@ namespace cryptonote std::string m_generate_from_json; std::string m_mnemonic_language; std::string m_import_path; + std::string m_subaddress_lookahead; std::string m_electrum_seed; // electrum-style seed parameter @@ -324,11 +336,11 @@ namespace cryptonote bool m_restoring; // are we restoring, by whatever method? uint64_t m_restore_height; // optional bool m_do_not_relay; + bool m_use_english_language_names; epee::console_handlers_binder m_cmd_binder; std::unique_ptr<tools::wallet2> m_wallet; - epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; std::atomic<bool> m_idle_run; |