diff options
Diffstat (limited to 'src/simplewallet/simplewallet.cpp')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 230 |
1 files changed, 216 insertions, 14 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 9f7cc9c3b..4692f7d59 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -235,6 +235,7 @@ namespace const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>"); const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>"); const char* USAGE_SET_RING("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"); + const char* USAGE_UNSET_RING("unset_ring <txid> | ( <key_image> [<key_image>...] )"); const char* USAGE_SAVE_KNOWN_RINGS("save_known_rings"); const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]"); const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>"); @@ -242,6 +243,7 @@ namespace const char* USAGE_FREEZE("freeze <key_image>"); const char* USAGE_THAW("thaw <key_image>"); const char* USAGE_FROZEN("frozen <key_image>"); + const char* USAGE_NET_STATS("net_stats"); const char* USAGE_VERSION("version"); const char* USAGE_HELP("help [<command>]"); @@ -1870,6 +1872,38 @@ bool simple_wallet::set_ring(const std::vector<std::string> &args) return true; } +bool simple_wallet::unset_ring(const std::vector<std::string> &args) +{ + crypto::hash txid; + std::vector<crypto::key_image> key_images; + + if (args.size() < 1) + { + PRINT_USAGE(USAGE_UNSET_RING); + return true; + } + + key_images.resize(args.size()); + for (size_t i = 0; i < args.size(); ++i) + { + if (!epee::string_tools::hex_to_pod(args[i], key_images[i])) + { + fail_msg_writer() << tr("Invalid key image or txid"); + return true; + } + } + static_assert(sizeof(crypto::hash) == sizeof(crypto::key_image), "hash and key_image must have the same size"); + memcpy(&txid, &key_images[0], sizeof(txid)); + + if (!m_wallet->unset_ring(key_images) && !m_wallet->unset_ring(txid)) + { + fail_msg_writer() << tr("failed to unset ring"); + return true; + } + + return true; +} + bool simple_wallet::blackball(const std::vector<std::string> &args) { uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets; @@ -2098,6 +2132,13 @@ bool simple_wallet::frozen(const std::vector<std::string> &args) return true; } +bool simple_wallet::net_stats(const std::vector<std::string> &args) +{ + message_writer() << std::to_string(m_wallet->get_bytes_sent()) + tr(" bytes sent"); + message_writer() << std::to_string(m_wallet->get_bytes_received()) + tr(" bytes received"); + return true; +} + bool simple_wallet::version(const std::vector<std::string> &args) { message_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; @@ -2592,6 +2633,31 @@ bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std: return true; } +bool simple_wallet::set_setup_background_mining(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + tools::wallet2::BackgroundMiningSetupType setup = tools::wallet2::BackgroundMiningMaybe; + if (args[1] == "yes" || args[1] == "1") + setup = tools::wallet2::BackgroundMiningYes; + else if (args[1] == "no" || args[1] == "0") + setup = tools::wallet2::BackgroundMiningNo; + else + { + fail_msg_writer() << tr("invalid argument: must be either 1/yes or 0/no"); + return true; + } + m_wallet->setup_background_mining(setup); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + if (setup == tools::wallet2::BackgroundMiningYes) + start_background_mining(); + else + stop_background_mining(); + } + return true; +} + bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -3069,6 +3135,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::set_ring, this, _1), tr(USAGE_SET_RING), tr("Set the ring used for a given key image, so it can be reused in a fork")); + m_cmd_binder.set_handler("unset_ring", + boost::bind(&simple_wallet::unset_ring, this, _1), + tr(USAGE_UNSET_RING), + tr("Unsets the ring used for a given key image or transaction")); m_cmd_binder.set_handler("save_known_rings", boost::bind(&simple_wallet::save_known_rings, this, _1), tr(USAGE_SAVE_KNOWN_RINGS), @@ -3097,6 +3167,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::frozen, this, _1), tr(USAGE_FROZEN), tr("Checks whether a given output is currently frozen by key image")); + m_cmd_binder.set_handler("net_stats", + boost::bind(&simple_wallet::net_stats, this, _1), + tr(USAGE_NET_STATS), + tr("Prints simple network stats")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), tr(USAGE_VERSION), @@ -3125,6 +3199,13 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) case tools::wallet2::AskPasswordOnAction: ask_password_string = "action"; break; case tools::wallet2::AskPasswordToDecrypt: ask_password_string = "decrypt"; break; } + std::string setup_background_mining_string = "invalid"; + switch (m_wallet->setup_background_mining()) + { + case tools::wallet2::BackgroundMiningMaybe: setup_background_mining_string = "maybe"; break; + case tools::wallet2::BackgroundMiningYes: setup_background_mining_string = "yes"; break; + case tools::wallet2::BackgroundMiningNo: setup_background_mining_string = "no"; break; + } 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(); @@ -3151,6 +3232,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs(); success_msg_writer() << "track-uses = " << m_wallet->track_uses(); + success_msg_writer() << "setup-background-mining = " << setup_background_mining_string + tr(" (set this to support the network and to get a chance to receive new monero)"); success_msg_writer() << "device_name = " << m_wallet->device_name(); return true; } @@ -3208,6 +3290,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); @@ -3403,6 +3486,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; epee::wipeable_string multisig_keys; + epee::wipeable_string password; if (!handle_command_line(vm)) return false; @@ -3510,7 +3594,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass); } } - epee::wipeable_string password; if (!m_generate_from_view_key.empty()) { m_wallet_file = m_generate_from_view_key; @@ -3955,8 +4038,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("can't specify --subaddress-lookahead and --wallet-file at the same time"); return false; } - bool r = open_wallet(vm); + auto r = open_wallet(vm); CHECK_AND_ASSERT_MES(r, false, tr("failed to open account")); + password = *r; } if (!m_wallet) { @@ -3972,6 +4056,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_wallet->callback(this); + check_background_mining(password); + return true; } //---------------------------------------------------------------------------------------------------- @@ -4341,12 +4427,12 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr return std::move(password); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) +boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::program_options::variables_map& vm) { if (!tools::wallet2::wallet_valid_path_format(m_wallet_file)) { fail_msg_writer() << tr("wallet file path not valid: ") << m_wallet_file; - return false; + return {}; } bool keys_file_exists; @@ -4356,7 +4442,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) if(!keys_file_exists) { fail_msg_writer() << tr("Key file not found. Failed to open wallet"); - return false; + return {}; } epee::wipeable_string password; @@ -4367,7 +4453,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) password = std::move(std::move(rc.second).password()); if (!m_wallet) { - return false; + return {}; } m_wallet->callback(this); @@ -4393,7 +4479,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) { bool is_deterministic; { - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return {};); is_deterministic = m_wallet->is_deterministic(); } if (is_deterministic) @@ -4402,7 +4488,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) "a deprecated version of the wallet. Please proceed to upgrade your wallet.\n"); std::string mnemonic_language = get_mnemonic_language(); if (mnemonic_language.empty()) - return false; + return {}; m_wallet->set_seed_language(mnemonic_language); m_wallet->rewrite(m_wallet_file, password); @@ -4434,14 +4520,14 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) if (password_is_correct) fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file; } - return false; + return {}; } success_msg_writer() << "**********************************************************************\n" << tr("Use the \"help\" command to see the list of available commands.\n") << tr("Use \"help <command>\" to see a command's documentation.\n") << "**********************************************************************"; - return true; + return std::move(password); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::close_wallet() @@ -4522,7 +4608,118 @@ bool simple_wallet::save_watch_only(const std::vector<std::string> &args/* = std } return true; } +//---------------------------------------------------------------------------------------------------- +void simple_wallet::start_background_mining() +{ + COMMAND_RPC_MINING_STATUS::request reqq; + COMMAND_RPC_MINING_STATUS::response resq; + bool r = m_wallet->invoke_http_json("/mining_status", reqq, resq); + std::string err = interpret_rpc_response(r, resq.status); + if (!r) + return; + if (!err.empty()) + { + fail_msg_writer() << tr("Failed to query mining status: ") << err; + return; + } + if (!resq.is_background_mining_enabled) + { + COMMAND_RPC_START_MINING::request req; + COMMAND_RPC_START_MINING::response res; + req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); + req.threads_count = 1; + req.do_background_mining = true; + req.ignore_battery = false; + bool r = m_wallet->invoke_http_json("/start_mining", req, res); + std::string err = interpret_rpc_response(r, res.status); + if (!err.empty()) + { + fail_msg_writer() << tr("Failed to setup background mining: ") << err; + return; + } + } + success_msg_writer() << tr("Background mining enabled. Thank you for supporting the Monero network."); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::stop_background_mining() +{ + COMMAND_RPC_MINING_STATUS::request reqq; + COMMAND_RPC_MINING_STATUS::response resq; + bool r = m_wallet->invoke_http_json("/mining_status", reqq, resq); + if (!r) + return; + std::string err = interpret_rpc_response(r, resq.status); + if (!err.empty()) + { + fail_msg_writer() << tr("Failed to query mining status: ") << err; + return; + } + if (resq.is_background_mining_enabled) + { + COMMAND_RPC_STOP_MINING::request req; + COMMAND_RPC_STOP_MINING::response res; + bool r = m_wallet->invoke_http_json("/stop_mining", req, res); + std::string err = interpret_rpc_response(r, res.status); + if (!err.empty()) + { + fail_msg_writer() << tr("Failed to setup background mining: ") << err; + return; + } + } + message_writer(console_color_red, false) << tr("Background mining not enabled. Run \"set setup-background-mining 1\" to change."); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::check_background_mining(const epee::wipeable_string &password) +{ + tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); + if (setup == tools::wallet2::BackgroundMiningNo) + { + message_writer(console_color_red, false) << tr("Background mining not enabled. Run \"set setup-background-mining 1\" to change."); + return; + } + if (!m_wallet->is_trusted_daemon()) + { + message_writer() << tr("Using an untrusted daemon, skipping background mining check"); + return; + } + + COMMAND_RPC_MINING_STATUS::request req; + COMMAND_RPC_MINING_STATUS::response res; + bool r = m_wallet->invoke_http_json("/mining_status", req, res); + std::string err = interpret_rpc_response(r, res.status); + bool is_background_mining_enabled = false; + if (err.empty()) + is_background_mining_enabled = res.is_background_mining_enabled; + + if (is_background_mining_enabled) + { + // already active, nice + m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes); + m_wallet->rewrite(m_wallet_file, password); + start_background_mining(); + return; + } + if (res.active) + return; + + if (setup == tools::wallet2::BackgroundMiningMaybe) + { + message_writer() << tr("The daemon is not set up to background mine."); + message_writer() << tr("With background mining enabled, the daemon will mine when idle and not on batttery."); + message_writer() << tr("Enabling this supports the network you are using, and makes you eligible for receiving new monero"); + std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): ")); + if (std::cin.eof() || !command_line::is_yes(accepted)) { + m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningNo); + m_wallet->rewrite(m_wallet_file, password); + message_writer(console_color_red, false) << tr("Background mining not enabled. Set setup-background-mining to 1 to change."); + return; + } + m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes); + m_wallet->rewrite(m_wallet_file, password); + start_background_mining(); + } +} //---------------------------------------------------------------------------------------------------- bool simple_wallet::start_mining(const std::vector<std::string>& args) { @@ -4948,10 +5145,15 @@ bool simple_wallet::show_balance_unlocked(bool detailed) success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); + uint64_t blocks_to_unlock; + uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, &blocks_to_unlock); + std::string unlock_time_message; + if (blocks_to_unlock > 0) + unlock_time_message = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock).str(); success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account)) << ", " - << tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance(m_current_subaddress_account)) << extra; + << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra; std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account); - std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); + std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); if (!detailed || balance_per_subaddress.empty()) return true; success_msg_writer() << tr("Balance per address:"); @@ -4963,7 +5165,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed) cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first}; std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6); uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == subaddr_index; }); - success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first]) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index); + success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index); } return true; } @@ -5297,7 +5499,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending } const cryptonote::tx_source_entry& source = *sptr; - ostr << boost::format(tr("\nInput %llu/%llu: amount=%s")) % (i + 1) % tx.vin.size() % print_money(source.amount); + ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount); // convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets); // get block heights from which those ring member keys originated |