// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers /*! * \file simplewallet.cpp * * \brief Source file that defines simple_wallet class. */ #include #include #include #include #include #include #include #include #include #include #include "include_base_utils.h" #include "common/i18n.h" #include "common/command_line.h" #include "common/util.h" #include "common/dns_utils.h" #include "common/base58.h" #include "common/scoped_message_writer.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "simplewallet.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" #include "crypto/crypto.h" // for crypto::secret_key definition #include "mnemonics/electrum-words.h" #include "rapidjson/document.h" #include "common/json_util.h" #include "ringct/rctSigs.h" #include "wallet/wallet_args.h" #include #ifdef HAVE_READLINE #include "readline_buffer.h" #endif using namespace std; using namespace epee; using namespace cryptonote; using boost::lexical_cast; namespace po = boost::program_options; typedef cryptonote::simple_wallet sw; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.simplewallet" #define EXTENDED_LOGS_FILE "wallet_details.log" #define DEFAULT_MIX 4 #define MIN_RING_SIZE 5 // Used to inform user about min ring size -- does not track actual protocol #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" #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); \ /* stop any background refresh, and take over */ \ m_wallet->stop(); \ m_idle_mutex.lock(); \ while (m_auto_refresh_refreshing) \ m_idle_cond.notify_one(); \ m_idle_mutex.unlock(); \ /* if (auto_refresh_run)*/ \ /*m_auto_refresh_thread.join();*/ \ boost::unique_lock lock(m_idle_mutex); \ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ }) enum TransferType { TransferOriginal, TransferNew, TransferLocked, }; namespace { const std::array allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}}; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const command_line::arg_descriptor arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to "), ""}; const command_line::arg_descriptor arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; const command_line::arg_descriptor arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""}; const command_line::arg_descriptor arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""}; const command_line::arg_descriptor arg_generate_from_multisig_keys = {"generate-from-multisig-keys", sw::tr("Generate a master wallet from multisig wallet keys"), ""}; const auto arg_generate_from_json = wallet_args::arg_generate_from_json(); const command_line::arg_descriptor arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""}; const command_line::arg_descriptor arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""}; const command_line::arg_descriptor arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false}; const command_line::arg_descriptor arg_non_deterministic = {"non-deterministic", sw::tr("Create non-deterministic view and spend keys"), false}; const command_line::arg_descriptor arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor 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 arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; const command_line::arg_descriptor 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< std::vector > arg_command = {"command", ""}; std::string input_line(const std::string& prompt) { #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; #endif std::cout << prompt; std::string buf; std::getline(std::cin, buf); return epee::string_tools::trim(buf); } boost::optional password_prompter(const char *prompt, bool verify) { #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; #endif auto pwd_container = tools::password_container::prompt(verify, prompt); if (!pwd_container) { tools::fail_msg_writer() << tr("failed to read wallet password"); } return pwd_container; } boost::optional default_password_prompter(bool verify) { return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify); } inline std::string interpret_rpc_response(bool ok, const std::string& status) { std::string err; if (ok) { if (status == CORE_RPC_STATUS_BUSY) { err = sw::tr("daemon is busy. Please try again later."); } else if (status != CORE_RPC_STATUS_OK) { err = status; } } else { err = sw::tr("possibly lost connection to daemon"); } return err; } tools::scoped_message_writer success_msg_writer(bool color = false) { return tools::scoped_message_writer(color ? console_color_green : console_color_default, false, std::string(), el::Level::Info); } tools::scoped_message_writer message_writer(epee::console_colors color = epee::console_color_default, bool bright = false) { return tools::scoped_message_writer(color, bright); } tools::scoped_message_writer fail_msg_writer() { return tools::scoped_message_writer(console_color_red, true, sw::tr("Error: "), el::Level::Error); } bool parse_bool(const std::string& s, bool& result) { if (s == "1" || command_line::is_yes(s)) { result = true; return true; } if (s == "0" || command_line::is_no(s)) { result = false; return true; } boost::algorithm::is_iequal ignore_case{}; if (boost::algorithm::equals("true", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case)) { result = true; return true; } if (boost::algorithm::equals("false", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("false"), s, ignore_case)) { result = false; return true; } return false; } template bool parse_bool_and_use(const std::string& s, F func) { bool r; if (parse_bool(s, r)) { func(r); return true; } else { fail_msg_writer() << tr("invalid argument: must be either 0/1, true/false, y/n, yes/no"); return false; } } const struct { const char *name; tools::wallet2::RefreshType refresh_type; } refresh_type_names[] = { { "full", tools::wallet2::RefreshFull }, { "optimize-coinbase", tools::wallet2::RefreshOptimizeCoinbase }, { "optimized-coinbase", tools::wallet2::RefreshOptimizeCoinbase }, { "no-coinbase", tools::wallet2::RefreshNoCoinbase }, { "default", tools::wallet2::RefreshDefault }, }; bool parse_refresh_type(const std::string &s, tools::wallet2::RefreshType &refresh_type) { for (size_t n = 0; n < sizeof(refresh_type_names) / sizeof(refresh_type_names[0]); ++n) { if (s == refresh_type_names[n].name) { refresh_type = refresh_type_names[n].refresh_type; return true; } } fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse refresh type"); return false; } std::string get_refresh_type_name(tools::wallet2::RefreshType type) { for (size_t n = 0; n < sizeof(refresh_type_names) / sizeof(refresh_type_names[0]); ++n) { if (type == refresh_type_names[n].refresh_type) return refresh_type_names[n].name; } return "invalid"; } std::string get_version_string(uint32_t version) { return boost::lexical_cast(version >> 16) + "." + boost::lexical_cast(version & 0xffff); } std::string oa_prompter(const std::string &url, const std::vector &addresses, bool dnssec_valid) { if (addresses.empty()) return {}; // prompt user for confirmation. // inform user of DNSSEC validation status as well. std::string dnssec_str; if (dnssec_valid) { dnssec_str = tr("DNSSEC validation passed"); } else { dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); } std::stringstream prompt; prompt << tr("For URL: ") << url << ", " << dnssec_str << std::endl << tr(" Monero Address = ") << addresses[0] << std::endl << tr("Is this OK? (Y/n) ") ; // prompt the user for confirmation given the dns query and dnssec status std::string confirm_dns_ok = input_line(prompt.str()); if (std::cin.eof()) { return {}; } if (!command_line::is_yes(confirm_dns_ok)) { std::cout << tr("you have cancelled the transfer request") << std::endl; return {}; } return addresses[0]; } bool parse_subaddress_indices(const std::string& arg, std::set& subaddr_indices) { subaddr_indices.clear(); if (arg.substr(0, 6) != "index=") return false; std::string subaddr_indices_str_unsplit = arg.substr(6, arg.size() - 6); std::vector subaddr_indices_str; boost::split(subaddr_indices_str, subaddr_indices_str_unsplit, boost::is_any_of(",")); for (const auto& subaddr_index_str : subaddr_indices_str) { uint32_t subaddr_index; if(!epee::string_tools::get_xtype_from_string(subaddr_index, subaddr_index_str)) { fail_msg_writer() << tr("failed to parse index: ") << subaddr_index_str; subaddr_indices.clear(); return false; } subaddr_indices.insert(subaddr_index); } return true; } } bool parse_priority(const std::string& arg, uint32_t& priority) { auto priority_pos = std::find( allowed_priority_strings.begin(), allowed_priority_strings.end(), arg); if(priority_pos != allowed_priority_strings.end()) { priority = std::distance(allowed_priority_strings.begin(), priority_pos); return true; } return false; } std::string simple_wallet::get_commands_str() { std::stringstream ss; ss << tr("Commands: ") << ENDL; std::string usage = m_cmd_binder.get_usage(); boost::replace_all(usage, "\n", "\n "); usage.insert(0, " "); ss << usage << ENDL; return ss.str(); } std::string simple_wallet::get_command_usage(const std::vector &args) { std::pair documentation = m_cmd_binder.get_documentation(args); std::stringstream ss; if(documentation.first.empty()) { ss << tr("Unknown command: ") << args.front(); } else { std::string usage = documentation.second.empty() ? args.front() : documentation.first; std::string description = documentation.second.empty() ? documentation.first : documentation.second; usage.insert(0, " "); ss << tr("Command usage: ") << ENDL << usage << ENDL << ENDL; boost::replace_all(description, "\n", "\n "); description.insert(0, " "); ss << tr("Command description: ") << ENDL << description << ENDL; } return ss.str(); } bool simple_wallet::viewkey(const std::vector &args/* = std::vector()*/) { if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } // don't log std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl; std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl; return true; } bool simple_wallet::spendkey(const std::vector &args/* = std::vector()*/) { if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and has no spend key"); return true; } if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } // don't log std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl; std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl; return true; } bool simple_wallet::print_seed(bool encrypted) { bool success = false; std::string electrum_words; if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and has no seed"); return true; } if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (m_wallet->is_deterministic()) { if (m_wallet->get_seed_language().empty()) { std::string mnemonic_language = get_mnemonic_language(); if (mnemonic_language.empty()) return true; m_wallet->set_seed_language(mnemonic_language); } epee::wipeable_string seed_pass; if (encrypted) { auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed")); if (std::cin.eof() || !pwd_container) return true; seed_pass = pwd_container->password(); } success = m_wallet->get_seed(electrum_words, seed_pass); } if (success) { print_seed(electrum_words); } else { fail_msg_writer() << tr("wallet is non-deterministic and has no seed"); } return true; } bool simple_wallet::seed(const std::vector &args/* = std::vector()*/) { return print_seed(false); } bool simple_wallet::encrypted_seed(const std::vector &args/* = std::vector()*/) { return print_seed(true); } bool simple_wallet::seed_set_language(const std::vector &args/* = std::vector()*/) { if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and has no seed"); return true; } if (!m_wallet->is_deterministic()) { fail_msg_writer() << tr("wallet is non-deterministic and has no seed"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { std::string mnemonic_language = get_mnemonic_language(); if (mnemonic_language.empty()) return true; m_wallet->set_seed_language(std::move(mnemonic_language)); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::change_password(const std::vector &args) { const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); return true; } // prompts for a new password, pass true to verify the password const auto pwd_container = default_password_prompter(true); try { m_wallet->rewrite(m_wallet_file, pwd_container->password()); m_wallet->store(); } catch (const tools::error::wallet_logic_error& e) { fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what(); return true; } return true; } bool simple_wallet::payment_id(const std::vector &args/* = std::vector()*/) { crypto::hash payment_id; if (args.size() > 0) { fail_msg_writer() << tr("usage: payment_id"); return true; } payment_id = crypto::rand(); success_msg_writer() << tr("Random payment ID: ") << payment_id; return true; } bool simple_wallet::print_fee_info(const std::vector &args/* = std::vector()*/) { if (!try_connect_to_daemon()) { fail_msg_writer() << tr("Cannot connect to daemon"); return true; } 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(); std::vector fees; for (uint32_t priority = 1; priority <= 4; ++priority) { uint64_t mult = m_wallet->get_fee_multiplier(priority); fees.push_back(per_kb_fee * typical_size_kb * mult); } std::vector> blocks; try { uint64_t base_size = typical_size_kb * 1024; blocks = m_wallet->estimate_backlog(base_size, base_size + 1023, fees); } catch (const std::exception &e) { fail_msg_writer() << tr("Error: failed to estimate backlog array size: ") << e.what(); return true; } if (blocks.size() != 4) { fail_msg_writer() << tr("Error: bad estimated backlog array size"); return true; } for (uint32_t priority = 1; priority <= 4; ++priority) { uint64_t nblocks_low = blocks[priority - 1].first; uint64_t nblocks_high = blocks[priority - 1].second; if (nblocks_low > 0) { std::string msg; if (priority == m_wallet->get_default_priority() || (m_wallet->get_default_priority() == 0 && priority == 2)) msg = tr(" (current)"); uint64_t minutes_low = nblocks_low * DIFFICULTY_TARGET_V2 / 60, minutes_high = nblocks_high * DIFFICULTY_TARGET_V2 / 60; if (nblocks_high == nblocks_low) message_writer() << (boost::format(tr("%u block (%u minutes) backlog at priority %u%s")) % nblocks_low % minutes_low % priority % msg).str(); else message_writer() << (boost::format(tr("%u to %u block (%u to %u minutes) backlog at priority %u")) % nblocks_low % nblocks_high % minutes_low % minutes_high % priority).str(); } else message_writer() << tr("No backlog at priority ") << priority; } return true; } bool simple_wallet::prepare_multisig(const std::vector &args) { if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); return true; } if(m_wallet->get_num_transfer_details()) { fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet"); return true; } const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your password is incorrect."); return true; } std::string multisig_info = m_wallet->get_multisig_info(); success_msg_writer() << multisig_info; success_msg_writer() << "Send this multisig info to all other participants, then use make_multisig [...] with others' multisig info"; success_msg_writer() << "This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants "; return true; } bool simple_wallet::make_multisig(const std::vector &args) { if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); return true; } if(m_wallet->get_num_transfer_details()) { fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet"); return true; } const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); return true; } if (args.size() < 2) { fail_msg_writer() << tr("usage: make_multisig [...]"); return true; } // parse threshold uint32_t threshold; if (!string_tools::get_xtype_from_string(threshold, args[0])) { fail_msg_writer() << tr("Invalid threshold"); return true; } // parse all multisig info std::vector secret_keys(args.size() - 1); std::vector public_keys(args.size() - 1); for (size_t i = 1; i < args.size(); ++i) { if (!tools::wallet2::verify_multisig_info(args[i], secret_keys[i - 1], public_keys[i - 1])) { fail_msg_writer() << tr("Bad multisig info: ") << args[i]; return true; } } // remove duplicates for (size_t i = 1; i < secret_keys.size(); ++i) { if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(secret_keys[0])) { message_writer() << tr("Duplicate key found, ignoring"); secret_keys[i] = secret_keys.back(); public_keys[i] = public_keys.back(); secret_keys.pop_back(); public_keys.pop_back(); --i; } } // people may include their own, weed it out for (size_t i = 0; i < secret_keys.size(); ++i) { if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(m_wallet->get_account().get_keys().m_view_secret_key)) { message_writer() << tr("Local key is present, ignoring"); secret_keys[i] = secret_keys.back(); public_keys[i] = public_keys.back(); secret_keys.pop_back(); public_keys.pop_back(); --i; } else if (rct::pk2rct(public_keys[i]) == rct::pk2rct(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key)) { fail_msg_writer() << "Found local spend public key, but not local view secret key - something very weird"; return true; } } LOCK_IDLE_SCOPE(); try { m_wallet->make_multisig(orig_pwd_container->password(), secret_keys, public_keys, threshold); } catch (const std::exception &e) { fail_msg_writer() << "Error creating multisig: " << e.what(); return true; } uint32_t total = secret_keys.size() + 1; success_msg_writer() << std::to_string(threshold) << "/" << total << " multisig address: " << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); return true; } bool simple_wallet::set_always_confirm_transfers(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->always_confirm_transfers(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_print_ring_members(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->print_ring_members(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_store_tx_info(const std::vector &args/* = std::vector()*/) { if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->store_tx_info(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_default_ring_size(const std::vector &args/* = std::vector()*/) { if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); return true; } try { if (strchr(args[1].c_str(), '-')) { fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; return true; } uint32_t ring_size = boost::lexical_cast(args[1]); if (ring_size < MIN_RING_SIZE && ring_size != 0) { fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } catch(const boost::bad_lexical_cast &) { fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; return true; } catch(...) { fail_msg_writer() << tr("could not change default ring size"); return true; } } bool simple_wallet::set_default_priority(const std::vector &args/* = std::vector()*/) { int priority = 0; try { if (strchr(args[1].c_str(), '-')) { fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 "); return true; } if (args[1] == "0") { priority = 0; } else { priority = boost::lexical_cast(args[1]); if (priority < 1 || priority > 4) { fail_msg_writer() << tr("priority must be 0, 1, 2, 3,or 4"); return true; } } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->set_default_priority(priority); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } catch(const boost::bad_lexical_cast &) { fail_msg_writer() << tr("priority must be 0, 1, 2 3,or 4"); return true; } catch(...) { fail_msg_writer() << tr("could not change default priority"); return true; } } bool simple_wallet::set_auto_refresh(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool auto_refresh) { m_wallet->auto_refresh(auto_refresh); m_idle_mutex.lock(); m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed); m_idle_cond.notify_one(); m_idle_mutex.unlock(); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_refresh_type(const std::vector &args/* = std::vector()*/) { tools::wallet2::RefreshType refresh_type; if (!parse_refresh_type(args[1], refresh_type)) { return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->set_refresh_type(refresh_type); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::set_confirm_missing_payment_id(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->confirm_missing_payment_id(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_ask_password(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->ask_password(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_unit(const std::vector &args/* = std::vector()*/) { const std::string &unit = args[1]; unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; if (unit == "monero") decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; else if (unit == "millinero") decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 3; else if (unit == "micronero") decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 6; else if (unit == "nanonero") decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 9; else if (unit == "piconero") decimal_point = 0; else { fail_msg_writer() << tr("invalid unit"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->set_default_decimal_point(decimal_point); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::set_min_output_count(const std::vector &args/* = std::vector()*/) { uint32_t count; if (!string_tools::get_xtype_from_string(count, args[1])) { fail_msg_writer() << tr("invalid count: must be an unsigned integer"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->set_min_output_count(count); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::set_min_output_value(const std::vector &args/* = std::vector()*/) { uint64_t value; if (!cryptonote::parse_amount(value, args[1])) { fail_msg_writer() << tr("invalid value"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->set_min_output_value(value); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::set_merge_destinations(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->merge_destinations(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_confirm_backlog(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { m_wallet->confirm_backlog(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); } return true; } bool simple_wallet::set_confirm_backlog_threshold(const std::vector &args/* = std::vector()*/) { uint32_t threshold; if (!string_tools::get_xtype_from_string(threshold, args[1])) { fail_msg_writer() << tr("invalid count: must be an unsigned integer"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { m_wallet->set_confirm_backlog_threshold(threshold); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::set_refresh_from_block_height(const std::vector &args/* = std::vector()*/) { 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->set_refresh_from_block_height(height); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } bool simple_wallet::help(const std::vector &args/* = std::vector()*/) { if(args.empty()) { success_msg_writer() << get_commands_str(); } else { success_msg_writer() << get_command_usage(args); } return true; } simple_wallet::simple_wallet() : m_allow_mismatched_daemon_version(false) , m_refresh_progress_reporter(*this) , m_idle_run(true) , m_auto_refresh_enabled(false) , m_auto_refresh_refreshing(false) , m_in_manual_refresh(false) , m_current_subaddress_account(0) { m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), tr("start_mining [] [bg_mining] [ignore_battery]"), tr("Start mining in the daemon (bg_mining and ignore_battery are optional booleans).")); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), tr("Stop mining in the daemon.")); m_cmd_binder.set_handler("set_daemon", boost::bind(&simple_wallet::set_daemon, this, _1), tr("set_daemon [:]"), tr("Set another daemon to connect to.")); m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::save_bc, this, _1), tr("Save the current blockchain data.")); m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), tr("Synchronize the transactions and balance.")); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), tr("balance [detail]"), tr("Show the wallet's balance of the currently selected account.")); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] [verbose] [index=[,[,...]]]"), tr("Show the incoming transfers, all or filtered by availability and address index.")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments [ ... ]"), tr("Show the payments for the given payment IDs.")); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show the blockchain height.")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer_original [index=[,,...]] [] []
[]"), tr("Transfer to
using an older transaction building algorithm. If the parameter \"index=[,,...]\" 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. is the priority of the transaction. The higher the priority, the higher the fee of the transaction. 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. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [index=[,,...]] [] []
[]"), tr("Transfer to
. If the parameter \"index=[,,...]\" 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. is the priority of the transaction. The higher the priority, the higher the fee of the transaction. 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. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [index=[,,...]] [] [] []"), tr("Transfer to
and lock it for (max. 1000000). If the parameter \"index=[,,...]\" 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. is the priority of the transaction. The higher the priority, the higher the fee of the transaction. 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. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [index=[,,...]] [] []
[]"), tr("Send all unlocked balance to an address. If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below [index=[,,...]] [] []
[]"), tr("Send all unlocked outputs below the threshold to an address.")); m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), tr("sweep_single [] []
[]"), tr("Send a single output of the given key image to an address without change.")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [index=[,,...]] [] [] []"), tr("Donate 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 "), tr("Sign a transaction from a .")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file.")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log |{+,-,}"), tr("Change the current log detail (level must be <0-4>).")); m_cmd_binder.set_handler("account", boost::bind(&simple_wallet::account, this, _1), tr("account [new