// Copyright (c) 2014-2023, 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. */ // use boost bind placeholders for now #define BOOST_BIND_GLOBAL_PLACEHOLDERS 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "include_base_utils.h" #include "console_handler.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 "multisig/multisig.h" #include "wallet/wallet_args.h" #include "version.h" #include #include "wallet/message_store.h" #include "QrCode.hpp" #ifdef WIN32 #include #include #include #endif #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 OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ /* stop any background refresh and other processes, and take over */ \ m_wallet->stop(); \ boost::unique_lock lock(m_idle_mutex); \ m_idle_cond.notify_all(); \ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ /* m_idle_mutex is still locked here */ \ m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ m_idle_cond.notify_one(); \ }) #define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \ LOCK_IDLE_SCOPE(); \ boost::optional pwd_container = boost::none; \ if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \ tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) #define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help; #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ do { \ fail_msg_writer() << tr("Error: Long payment IDs are obsolete."); \ fail_msg_writer() << tr("Long payment IDs were not encrypted on the blockchain and would harm your privacy."); \ fail_msg_writer() << tr("If the party you're sending to still requires a long payment ID, please notify them."); \ return true; \ } while(0) #define REFRESH_PERIOD 90 // seconds #define MAX_MNEW_ADDRESSES 1000 #define CHECK_MULTISIG_ENABLED() \ do \ { \ if (!m_wallet->is_multisig_enabled()) \ { \ fail_msg_writer() << tr("Multisig is disabled."); \ fail_msg_writer() << tr("Multisig is an experimental feature and may have bugs. Things that could go wrong include: funds sent to a multisig wallet can't be spent at all, can only be spent with the participation of a malicious group member, or can be stolen by a malicious group member."); \ fail_msg_writer() << tr("You can enable it with:"); \ fail_msg_writer() << tr(" set enable-multisig-experimental 1"); \ return false; \ } \ } while(0) enum TransferType { Transfer, TransferLocked, }; static std::string get_human_readable_timespan(std::chrono::seconds seconds); static std::string get_human_readable_timespan(uint64_t seconds); 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_device = {"generate-from-device", sw::tr("Generate new wallet from device 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_restore_from_seed = {"restore-from-seed", sw::tr("alias for --restore-deterministic-wallet"), false}; const command_line::arg_descriptor arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false}; const command_line::arg_descriptor arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), 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_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""}; 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 arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to :"), ""}; const command_line::arg_descriptor arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false}; const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; const char* USAGE_START_MINING("start_mining [] [bg_mining] [ignore_battery]"); const char* USAGE_SET_DAEMON("set_daemon [:] [trusted|untrusted|this-is-probably-a-spy-node]"); const char* USAGE_SHOW_BALANCE("balance [detail]"); const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=[,[,...]]]"); const char* USAGE_PAYMENTS("payments [ ... ]"); const char* USAGE_PAYMENT_ID("payment_id"); const char* USAGE_TRANSFER("transfer [index=[,,...]] [] [] ( |
) [subtractfeefrom=[,,all,...]] []"); const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=[,,...]] [] [] ( | ) []"); const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=[,,...] | index=all] [] []
[]"); const char* USAGE_SWEEP_ALL("sweep_all [index=[,,...] | index=all] [] [] [outputs=]
[]"); const char* USAGE_SWEEP_ACCOUNT("sweep_account [index=[,,...] | index=all] [] [] [outputs=]
[]"); const char* USAGE_SWEEP_BELOW("sweep_below [index=[,,...]] [] []
[]"); const char* USAGE_SWEEP_SINGLE("sweep_single [] [] [outputs=]
[]"); const char* USAGE_DONATE("donate [index=[,,...]] [] [] []"); const char* USAGE_SIGN_TRANSFER("sign_transfer [export_raw] []"); const char* USAGE_SET_LOG("set_log |{+,-,}"); const char* USAGE_ACCOUNT("account\n" " account new