From 358e068e878892eb3cc0f333215708390e51f0c3 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Tue, 8 Nov 2016 22:55:41 -0500 Subject: Created monero-wallet-rpc, moving functionality from monero-wallet-cli --- src/simplewallet/CMakeLists.txt | 6 +- src/simplewallet/password_container.cpp | 301 ------------- src/simplewallet/password_container.h | 64 --- src/simplewallet/simplewallet.cpp | 762 +++++--------------------------- src/simplewallet/simplewallet.h | 21 +- src/wallet/CMakeLists.txt | 39 ++ src/wallet/password_container.cpp | 301 +++++++++++++ src/wallet/password_container.h | 64 +++ src/wallet/wallet2.cpp | 344 +++++++++++++- src/wallet/wallet2.h | 19 + src/wallet/wallet_args.cpp | 185 ++++++++ src/wallet/wallet_args.h | 53 +++ src/wallet/wallet_rpc_server.cpp | 127 +++++- src/wallet/wallet_rpc_server.h | 9 +- 14 files changed, 1241 insertions(+), 1054 deletions(-) delete mode 100644 src/simplewallet/password_container.cpp delete mode 100644 src/simplewallet/password_container.h create mode 100644 src/wallet/password_container.cpp create mode 100644 src/wallet/password_container.h create mode 100644 src/wallet/wallet_args.cpp create mode 100644 src/wallet/wallet_args.h (limited to 'src') diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index cb9ba2b7c..259008ac5 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -27,14 +27,12 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(simplewallet_sources - simplewallet.cpp - password_container.cpp) + simplewallet.cpp) set(simplewallet_headers) set(simplewallet_private_headers - simplewallet.h - password_container.h) + simplewallet.h) monero_private_headers(simplewallet ${simplewallet_private_headers}) diff --git a/src/simplewallet/password_container.cpp b/src/simplewallet/password_container.cpp deleted file mode 100644 index 480d132e7..000000000 --- a/src/simplewallet/password_container.cpp +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright (c) 2014-2016, 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 - -#include "password_container.h" - -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#endif - -namespace tools -{ - namespace - { - bool is_cin_tty(); - } - // deleted via private member - password_container::password_container() - : m_empty(true),m_verify(true) - { - - } - password_container::password_container(bool verify) - : m_empty(true),m_verify(verify) - { - - } - - password_container::password_container(std::string&& password) - : m_empty(false) - , m_password(std::move(password)) - , m_verify(false) - { - - } - - - password_container::password_container(password_container&& rhs) - : m_empty(std::move(rhs.m_empty)) - , m_password(std::move(rhs.m_password)) - , m_verify(std::move(rhs.m_verify)) - { - } - password_container::~password_container() - { - clear(); - } - - void password_container::clear() - { - if (0 < m_password.capacity()) - { - m_password.replace(0, m_password.capacity(), m_password.capacity(), '\0'); - m_password.resize(0); - } - m_empty = true; - } - - bool password_container::read_password(const char *message) - { - clear(); - - bool r; - if (is_cin_tty()) - { - r = read_from_tty_double_check(message); - } - else - { - r = read_from_file(); - } - - if (r) - { - m_empty = false; - } - else - { - clear(); - } - - return r; - } - - bool password_container::read_from_file() - { - m_password.reserve(max_password_size); - for (size_t i = 0; i < max_password_size; ++i) - { - char ch = static_cast(std::cin.get()); - if (std::cin.eof() || ch == '\n' || ch == '\r') - { - break; - } - else if (std::cin.fail()) - { - return false; - } - else - { - m_password.push_back(ch); - } - } - - return true; - } - -bool password_container::read_from_tty_double_check(const char *message) { - std::string pass1; - std::string pass2; - bool match=false; - bool doNotVerifyEntry=false; - do{ - if (message) - std::cout << message <<": "; - if (!password_container::read_from_tty(pass1)) - return false; - if (m_verify==true){//double check password; - if (message) - std::cout << message << ": "; - if (!password_container::read_from_tty(pass2)) - return false; - if(pass1!=pass2){ //new password entered did not match - - std::cout << "Passwords do not match" << std::endl; - pass1=""; - pass2=""; - match=false; - } - else{//new password matches - match=true; - } - } - else - doNotVerifyEntry=true; //do not verify - //No need to verify password entered at this point in the code - - }while(match==false && doNotVerifyEntry==false); - - m_password=pass1; - return true; - } - - -#if defined(_WIN32) - - namespace - { - bool is_cin_tty() - { - return 0 != _isatty(_fileno(stdin)); - } - } - - bool password_container::read_from_tty(std::string & pass) - { - const char BACKSPACE = 8; - - HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE); - - DWORD mode_old; - ::GetConsoleMode(h_cin, &mode_old); - DWORD mode_new = mode_old & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); - ::SetConsoleMode(h_cin, mode_new); - - bool r = true; - pass.reserve(max_password_size); - while (pass.size() < max_password_size) - { - DWORD read; - char ch; - r = (TRUE == ::ReadConsoleA(h_cin, &ch, 1, &read, NULL)); - r &= (1 == read); - if (!r) - { - break; - } - else if (ch == '\n' || ch == '\r') - { - std::cout << std::endl; - break; - } - else if (ch == BACKSPACE) - { - if (!pass.empty()) - { - pass.back() = '\0'; - pass.resize(pass.size() - 1); - std::cout << "\b \b"; - } - } - else - { - pass.push_back(ch); - std::cout << '*'; - } - } - - ::SetConsoleMode(h_cin, mode_old); - - return r; - } - -#else - - namespace - { - bool is_cin_tty() - { - return 0 != isatty(fileno(stdin)); - } - - int getch() - { - struct termios tty_old; - tcgetattr(STDIN_FILENO, &tty_old); - - struct termios tty_new; - tty_new = tty_old; - tty_new.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &tty_new); - - int ch = getchar(); - - tcsetattr(STDIN_FILENO, TCSANOW, &tty_old); - - return ch; - } - } - bool password_container::read_from_tty(std::string &aPass) - { - const char BACKSPACE = 127; - - aPass.reserve(max_password_size); - while (aPass.size() < max_password_size) - { - int ch = getch(); - if (EOF == ch) - { - return false; - } - else if (ch == '\n' || ch == '\r') - { - std::cout << std::endl; - break; - } - else if (ch == BACKSPACE) - { - if (!aPass.empty()) - { - aPass.back() = '\0'; - aPass.resize(aPass.size() - 1); - std::cout << "\b \b"; - } - } - else - { - aPass.push_back(ch); - std::cout << '*'; - } - } - - return true; - } - -#endif -} diff --git a/src/simplewallet/password_container.h b/src/simplewallet/password_container.h deleted file mode 100644 index 62f43aa37..000000000 --- a/src/simplewallet/password_container.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2014-2016, 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 - -#pragma once - -#include -#include - -namespace tools -{ - class password_container - { - public: - static const size_t max_password_size = 1024; - password_container(bool verify); - password_container(password_container&& rhs); - password_container(std::string&& password); - ~password_container(); - - void clear(); - bool empty() const { return m_empty; } - const std::string& password() const { return m_password; } - void password(std::string&& val) { m_password = std::move(val); m_empty = false; } - bool read_password(const char *message = "password"); - - private: - //delete constructor with no parameters - password_container(); - bool read_from_file(); - bool read_from_tty(std::string & pass); - bool read_from_tty_double_check(const char *message); - - bool m_empty; - std::string m_password; - bool m_verify; - }; -} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ce1dac71a..a2b9405c8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -52,25 +52,19 @@ #include "cryptonote_core/cryptonote_format_utils.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "wallet/wallet_rpc_server.h" -#include "version.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 -#if defined(WIN32) -#include -#endif - using namespace std; using namespace epee; using namespace cryptonote; using boost::lexical_cast; namespace po = boost::program_options; -namespace bf = boost::filesystem; typedef cryptonote::simple_wallet sw; #define EXTENDED_LOGS_FILE "wallet_details.log" @@ -80,13 +74,6 @@ typedef cryptonote::simple_wallet sw; #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\002" -// workaround for a suspected bug in pthread/kernel on MacOS X -#ifdef __APPLE__ -#define DEFAULT_MAX_CONCURRENCY 1 -#else -#define DEFAULT_MAX_CONCURRENCY 0 -#endif - #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); \ @@ -111,24 +98,14 @@ enum TransferType { namespace { - const command_line::arg_descriptor arg_wallet_file = {"wallet-file", sw::tr("Use wallet "), ""}; + 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_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""}; - const command_line::arg_descriptor arg_generate_from_json = {"generate-from-json", sw::tr("Generate wallet from JSON format file"), ""}; - const command_line::arg_descriptor arg_daemon_address = {"daemon-address", sw::tr("Use daemon instance at :"), ""}; - const command_line::arg_descriptor arg_daemon_host = {"daemon-host", sw::tr("Use daemon instance at host instead of localhost"), ""}; - const command_line::arg_descriptor arg_password = {"password", sw::tr("Wallet password"), "", true}; - const command_line::arg_descriptor arg_password_file = {"password-file", sw::tr("Wallet password file"), "", true}; + const auto arg_generate_from_json = wallet_args::arg_generate_from_json(); 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_daemon_port = {"daemon-port", sw::tr("Use daemon instance at port instead of 18081"), 0}; - const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; - const command_line::arg_descriptor arg_max_concurrency = {"max-concurrency", "Max number of threads to use for a parallel job", DEFAULT_MAX_CONCURRENCY}; - const command_line::arg_descriptor arg_log_file = {"log-file", sw::tr("Specify log file"), ""}; - const command_line::arg_descriptor arg_testnet = {"testnet", sw::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; - const command_line::arg_descriptor arg_restricted = {"restricted-rpc", sw::tr("Restricts RPC to view-only commands"), 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}; @@ -272,7 +249,7 @@ namespace return true; } } - fail_msg_writer() << tr("failed to parse refresh type"); + fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse refresh type"); return false; } @@ -661,7 +638,6 @@ bool simple_wallet::help(const std::vector &args/* = std::vector current_version) { - fail_msg_writer() << boost::format(tr("Version %u too new, we can only grok up to %u")) % field_version % current_version; - return false; - } - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string()); - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0); - bool recover = field_scan_from_height_found; - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string()); - password = field_password; - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, viewkey, std::string, String, false, std::string()); - crypto::secret_key viewkey; - if (field_viewkey_found) - { - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data)) - { - fail_msg_writer() << tr("failed to parse view key secret key"); - return false; - } - viewkey = *reinterpret_cast(viewkey_data.data()); - crypto::public_key pkey; - if (!crypto::secret_key_to_public_key(viewkey, pkey)) { - fail_msg_writer() << tr("failed to verify view key secret key"); - return false; - } - } - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, spendkey, std::string, String, false, std::string()); - crypto::secret_key spendkey; - if (field_spendkey_found) - { - cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data)) - { - fail_msg_writer() << tr("failed to parse spend key secret key"); - return false; - } - spendkey = *reinterpret_cast(spendkey_data.data()); - crypto::public_key pkey; - if (!crypto::secret_key_to_public_key(spendkey, pkey)) { - fail_msg_writer() << tr("failed to verify spend key secret key"); - return false; - } - } - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed, std::string, String, false, std::string()); - std::string old_language; - if (field_seed_found) - { - if (!crypto::ElectrumWords::words_to_bytes(field_seed, m_recovery_key, old_language)) - { - fail_msg_writer() << tr("Electrum-style word list failed verification"); - return false; - } - m_electrum_seed = field_seed; - m_restore_deterministic_wallet = true; - } - - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string()); - - // compatibility checks - if (!field_seed_found && !field_viewkey_found) - { - fail_msg_writer() << tr("At least one of Electrum-style word list and private view key must be specified"); - return false; - } - if (field_seed_found && (field_viewkey_found || field_spendkey_found)) - { - fail_msg_writer() << tr("Both Electrum-style word list and private key(s) specified"); - return false; - } - - // if an address was given, we check keys against it, and deduce the spend - // public key if it was not given - if (field_address_found) - { - cryptonote::account_public_address address; - bool has_payment_id; - crypto::hash8 new_payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, testnet, field_address)) - { - fail_msg_writer() << tr("invalid address"); - return false; - } - if (field_viewkey_found) - { - crypto::public_key pkey; - if (!crypto::secret_key_to_public_key(viewkey, pkey)) { - fail_msg_writer() << tr("failed to verify view key secret key"); - return false; - } - if (address.m_view_public_key != pkey) { - fail_msg_writer() << tr("view key does not match standard address"); - return false; - } - } - if (field_spendkey_found) - { - crypto::public_key pkey; - if (!crypto::secret_key_to_public_key(spendkey, pkey)) { - fail_msg_writer() << tr("failed to verify spend key secret key"); - return false; - } - if (address.m_spend_public_key != pkey) { - fail_msg_writer() << tr("spend key does not match standard address"); - return false; - } - } - } - - m_wallet_file=field_filename; - - 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)); - if (was_deprecated_wallet) { - fail_msg_writer() << tr("Cannot create deprecated wallets from JSON"); - return false; - } - - m_wallet.reset(new tools::wallet2(testnet)); - m_wallet->callback(this); - m_wallet->set_refresh_from_block_height(field_scan_from_height); - - try - { - if (!field_seed.empty()) - { - m_wallet->generate(m_wallet_file, password, m_recovery_key, recover, false); - } - else - { - cryptonote::account_public_address address; - if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) { - fail_msg_writer() << tr("failed to verify view key secret key"); - return false; - } - - if (field_spendkey.empty()) - { - // if we have an addres but no spend key, we can deduce the spend public key - // from the address - if (field_address_found) - { - cryptonote::account_public_address address2; - bool has_payment_id; - crypto::hash8 new_payment_id; - get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address); - address.m_spend_public_key = address2.m_spend_public_key; - } - m_wallet->generate(m_wallet_file, password, address, viewkey); - } - else - { - if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) { - fail_msg_writer() << tr("failed to verify spend key secret key"); - return false; - } - m_wallet->generate(m_wallet_file, password, address, spendkey, viewkey); - } - } - } - catch (const std::exception& e) - { - fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); - return false; - } - - wallet_file = m_wallet_file; - - return r; -} - -static bool is_local_daemon(const std::string &address) -{ - // extract host - epee::net_utils::http::url_content u_c; - if (!epee::net_utils::parse_url(address, u_c)) - { - LOG_PRINT_L1("Failed to determine whether daemon is local, assuming not"); - return false; - } - if (u_c.host.empty()) - { - LOG_PRINT_L1("Failed to determine whether daemon is local, assuming not"); - return false; - } - - // resolve to IP - boost::asio::io_service io_service; - boost::asio::ip::tcp::resolver resolver(io_service); - boost::asio::ip::tcp::resolver::query query(u_c.host, ""); - boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query); - while (i != boost::asio::ip::tcp::resolver::iterator()) - { - const boost::asio::ip::tcp::endpoint &ep = *i; - if (ep.address().is_loopback()) - return true; - ++i; - } - - return false; -} - //---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { if (!handle_command_line(vm)) return false; - if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port) - { - fail_msg_writer() << tr("can't specify daemon host or port more than once"); - return false; - } - if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_json.empty()) > 1) { fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-keys=\"wallet_name\""); @@ -1273,37 +958,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if(!ask_wallet_create_if_needed()) return false; } - bool testnet = command_line::get_arg(vm, arg_testnet); - - if (m_daemon_host.empty()) - m_daemon_host = "localhost"; - - if (!m_daemon_port) - { - m_daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; - } - - if (m_daemon_address.empty()) - m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port); - - // set --trusted-daemon if local - try - { - if (is_local_daemon(m_daemon_address)) - { - LOG_PRINT_L1(tr("Daemon is local, assuming trusted")); - m_trusted_daemon = true; - } - } - catch (const std::exception &e) { } - tools::password_container pwd_container(m_wallet_file.empty()); //m_wallet_file will be empty at this point for new wallets - if (!cryptonote::simple_wallet::get_password(vm, true, pwd_container)) - return false; - if (!m_generate_new.empty() || m_restoring) { - if (m_wallet_file.empty()) m_wallet_file = m_generate_new; // alias for simplicity later - std::string old_language; // check for recover flag. if present, require electrum word list (only recovery option for now). if (m_restore_deterministic_wallet) @@ -1350,6 +1006,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } if (!m_generate_from_view_key.empty()) { + m_wallet_file = m_generate_from_view_key; // parse address std::string address_string = command_line::input_line("Standard address: "); if (std::cin.eof()) @@ -1361,7 +1018,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 new_payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, testnet, address_string)) + if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, tools::wallet2::has_testnet_option(vm), address_string)) { fail_msg_writer() << tr("failed to parse address"); return false; @@ -1396,11 +1053,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } - bool r = new_wallet(m_wallet_file, pwd_container.password(), address, viewkey, testnet); + bool r = new_wallet(vm, address, boost::none, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } else if (!m_generate_from_keys.empty()) { + m_wallet_file = m_generate_from_keys; // parse address std::string address_string = command_line::input_line("Standard address: "); if (std::cin.eof()) @@ -1412,7 +1070,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 new_payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, testnet, address_string)) + if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, tools::wallet2::has_testnet_option(vm), address_string)) { fail_msg_writer() << tr("failed to parse address"); return false; @@ -1470,29 +1128,30 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("view key does not match standard address"); return false; } - - bool r = new_wallet(m_wallet_file, pwd_container.password(), address, spendkey, viewkey, testnet); + bool r = new_wallet(vm, address, spendkey, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } else if (!m_generate_from_json.empty()) { - std::string wallet_file, password; // we don't need to remember them - if (!generate_from_json(vm, wallet_file, password)) + m_wallet_file = m_generate_from_json; + if (!tools::wallet2::make_from_json(vm, m_wallet_file)) return false; } else { - bool r = new_wallet(m_wallet_file, pwd_container.password(), m_recovery_key, m_restore_deterministic_wallet, - m_non_deterministic, testnet, old_language); + m_wallet_file = m_generate_new; + bool 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")); } } else { - bool r = open_wallet(m_wallet_file, pwd_container.password(), testnet); + assert(!m_wallet_file.empty()); + bool r = open_wallet(vm); CHECK_AND_ASSERT_MES(r, false, tr("failed to open account")); } - + assert(m_wallet); + m_wallet->callback(this); return true; } //---------------------------------------------------------------------------------------------------- @@ -1511,9 +1170,6 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key); m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys); m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json); - m_daemon_address = command_line::get_arg(vm, arg_daemon_address); - m_daemon_host = command_line::get_arg(vm, arg_daemon_host); - m_daemon_port = command_line::get_arg(vm, arg_daemon_port); m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed); m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet); m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic); @@ -1534,7 +1190,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent) if (!m_wallet->check_connection(&same_version)) { if (!silent) - fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_daemon_address << ". " << + fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " << tr("Daemon either is not started or wrong port was passed. " "Please make sure daemon is running or restart the wallet with the correct daemon address."); return false; @@ -1542,7 +1198,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent) if (!m_allow_mismatched_daemon_version && !same_version) { if (!silent) - fail_msg_writer() << tr("Daemon uses a different RPC version that the wallet: ") << m_daemon_address << ". " << + fail_msg_writer() << tr("Daemon uses a different RPC version that the wallet: ") << m_wallet->get_daemon_address() << ". " << tr("Either update one of them, or use --allow-mismatched-daemon-version."); return false; } @@ -1592,9 +1248,16 @@ std::string simple_wallet::get_mnemonic_language() } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password, const crypto::secret_key& recovery_key, - bool recover, bool two_random, bool testnet, const std::string &old_language) +bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, + const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language) { + auto rc = tools::wallet2::make_new(vm); + m_wallet = std::move(rc.first); + if (!m_wallet) + { + return false; + } + 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)); @@ -1616,11 +1279,6 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string return false; } - - m_wallet_file=wallet_file; - - m_wallet.reset(new tools::wallet2(testnet)); - m_wallet->callback(this); m_wallet->set_seed_language(mnemonic_language); // for a totally new account, we don't care about older blocks. @@ -1636,7 +1294,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string crypto::secret_key recovery_val; try { - recovery_val = m_wallet->generate(wallet_file, password, recovery_key, recover, two_random); + recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random); message_writer(epee::log_space::console_color_white, true) << tr("Generated new wallet: ") << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); std::cout << tr("View key: ") << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << ENDL; @@ -1647,8 +1305,6 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string return false; } - m_wallet->init(m_daemon_address); - // convert rng value to electrum-style word list std::string electrum_words; @@ -1673,47 +1329,29 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, - const crypto::secret_key& viewkey, bool testnet) +bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, + const cryptonote::account_public_address& address, const boost::optional& spendkey, + const crypto::secret_key& viewkey) { - m_wallet_file=wallet_file; - - m_wallet.reset(new tools::wallet2(testnet)); - m_wallet->callback(this); - if (m_restore_height) - m_wallet->set_refresh_from_block_height(m_restore_height); - - try + auto rc = tools::wallet2::make_new(vm); + m_wallet = std::move(rc.first); + if (!m_wallet) { - m_wallet->generate(wallet_file, password, address, viewkey); - message_writer(epee::log_space::console_color_white, true) << tr("Generated new watch-only wallet: ") - << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); - std::cout << tr("View key: ") << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << ENDL; - } - catch (const std::exception& e) - { - fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); return false; } - - m_wallet->init(m_daemon_address); - - return true; -} -//---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, - const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool testnet) -{ - m_wallet_file=wallet_file; - - m_wallet.reset(new tools::wallet2(testnet)); - m_wallet->callback(this); if (m_restore_height) m_wallet->set_refresh_from_block_height(m_restore_height); try { - m_wallet->generate(wallet_file, password, address, spendkey, viewkey); + if (spendkey) + { + m_wallet->generate(m_wallet_file, std::move(rc.second).password(), address, *spendkey, viewkey); + } + else + { + m_wallet->generate(m_wallet_file, std::move(rc.second).password(), address, viewkey); + } message_writer(epee::log_space::console_color_white, true) << tr("Generated new wallet: ") << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); } @@ -1723,26 +1361,28 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string return false; } - m_wallet->init(m_daemon_address); return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::open_wallet(const string &wallet_file, const std::string& password, bool testnet) +bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) { - if (!tools::wallet2::wallet_valid_path_format(wallet_file)) + if (!tools::wallet2::wallet_valid_path_format(m_wallet_file)) { - fail_msg_writer() << tr("wallet file path not valid: ") << wallet_file; + fail_msg_writer() << tr("wallet file path not valid: ") << m_wallet_file; return false; } - - m_wallet_file=wallet_file; - m_wallet.reset(new tools::wallet2(testnet)); - m_wallet->callback(this); - + std::string password; try { - m_wallet->load(m_wallet_file, password); + auto rc = tools::wallet2::make_from_file(vm, m_wallet_file); + m_wallet = std::move(rc.first); + password = std::move(rc.second).password(); + if (!m_wallet) + { + return false; + } + message_writer(epee::log_space::console_color_white, true) << (m_wallet->watch_only() ? tr("Opened watch-only wallet") : tr("Opened wallet")) << ": " << m_wallet->get_account().get_public_address_str(m_wallet->testnet()); @@ -1770,7 +1410,7 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa { message_writer(epee::log_space::console_color_green, false) << "\n" << tr("You had been using " "a deprecated version of the wallet. Your wallet file format is being upgraded now.\n"); - m_wallet->rewrite(m_wallet_file, password); + m_wallet->rewrite(m_wallet_file, password); } } } @@ -1778,13 +1418,10 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa { fail_msg_writer() << tr("failed to load wallet: ") << e.what(); // only suggest removing cache if the password was actually correct - if (m_wallet->verify_password(password)) - fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % wallet_file; + if (m_wallet && m_wallet->verify_password(password)) + fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file; return false; } - - m_wallet->init(m_daemon_address); - success_msg_writer() << "**********************************************************************\n" << tr("Use \"help\" command to see the list of available commands.\n") << @@ -1881,6 +1518,7 @@ bool simple_wallet::start_mining(const std::vector& args) if (!try_connect_to_daemon()) return true; + assert(m_wallet); COMMAND_RPC_START_MINING::request req; req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); @@ -1910,7 +1548,7 @@ bool simple_wallet::start_mining(const std::vector& args) } COMMAND_RPC_START_MINING::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client); + bool r = net_utils::invoke_http_json_remote_command2(m_wallet->get_daemon_address() + "/start_mining", req, res, m_http_client); std::string err = interpret_rpc_response(r, res.status); if (err.empty()) success_msg_writer() << tr("Mining started in daemon"); @@ -1924,9 +1562,10 @@ bool simple_wallet::stop_mining(const std::vector& args) if (!try_connect_to_daemon()) return true; + assert(m_wallet); COMMAND_RPC_STOP_MINING::request req; COMMAND_RPC_STOP_MINING::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client); + bool r = net_utils::invoke_http_json_remote_command2(m_wallet->get_daemon_address() + "/stop_mining", req, res, m_http_client); std::string err = interpret_rpc_response(r, res.status); if (err.empty()) success_msg_writer() << tr("Mining stopped in daemon"); @@ -1940,9 +1579,10 @@ bool simple_wallet::save_bc(const std::vector& args) if (!try_connect_to_daemon()) return true; + assert(m_wallet); COMMAND_RPC_SAVE_BC::request req; COMMAND_RPC_SAVE_BC::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/save_bc", req, res, m_http_client); + bool r = net_utils::invoke_http_json_remote_command2(m_wallet->get_daemon_address() + "/save_bc", req, res, m_http_client); std::string err = interpret_rpc_response(r, res.status); if (err.empty()) success_msg_writer() << tr("Blockchain saved"); @@ -2207,9 +1847,14 @@ bool simple_wallet::show_payments(const std::vector &args) //---------------------------------------------------------------------------------------------------- uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) { + if (!m_wallet) + { + throw std::runtime_error("simple_wallet null wallet"); + } + COMMAND_RPC_GET_HEIGHT::request req; COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized(); - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client); + bool r = net_utils::invoke_http_json_remote_command2(m_wallet->get_daemon_address() + "/getheight", req, res, m_http_client); err = interpret_rpc_response(r, res.status); return res.height; } @@ -3418,6 +3063,7 @@ bool simple_wallet::check_tx_key(const std::vector &args_) if (!try_connect_to_daemon()) return true; + assert(m_wallet); cryptonote::blobdata txid_data; if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data)) { @@ -3454,7 +3100,7 @@ bool simple_wallet::check_tx_key(const std::vector &args_) COMMAND_RPC_GET_TRANSACTIONS::request req; COMMAND_RPC_GET_TRANSACTIONS::response res; req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); - if (!net_utils::invoke_http_json_remote_command2(m_daemon_address + "/gettransactions", req, res, m_http_client) || + if (!net_utils::invoke_http_json_remote_command2(m_wallet->get_daemon_address() + "/gettransactions", req, res, m_http_client) || (res.txs.size() != 1 && res.txs_as_hex.size() != 1)) { fail_msg_writer() << tr("failed to get transaction from daemon"); @@ -4261,258 +3907,68 @@ void simple_wallet::interrupt() //---------------------------------------------------------------------------------------------------- int main(int argc, char* argv[]) { -#ifdef WIN32 - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif - - //TRY_ENTRY(); - - std::string lang = i18n_get_language(); - tools::sanitize_locale(); - tools::set_strict_default_file_permissions(true); - - string_tools::set_module_name_and_folder(argv[0]); - - po::options_description desc_general(sw::tr("General options")); - command_line::add_arg(desc_general, command_line::arg_help); - command_line::add_arg(desc_general, command_line::arg_version); - - po::options_description desc_params(sw::tr("Wallet options")); + po::options_description desc_params(wallet_args::tr("Wallet options")); + tools::wallet2::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_generate_new_wallet); command_line::add_arg(desc_params, arg_generate_from_view_key); command_line::add_arg(desc_params, arg_generate_from_keys); command_line::add_arg(desc_params, arg_generate_from_json); - command_line::add_arg(desc_params, arg_password); - command_line::add_arg(desc_params, arg_password_file); - command_line::add_arg(desc_params, arg_daemon_address); - command_line::add_arg(desc_params, arg_daemon_host); - command_line::add_arg(desc_params, arg_daemon_port); command_line::add_arg(desc_params, arg_command); - command_line::add_arg(desc_params, arg_log_level); - command_line::add_arg(desc_params, arg_max_concurrency); - - bf::path default_log {log_space::log_singletone::get_default_log_folder()}; - std::string log_file_name = log_space::log_singletone::get_default_log_file(); - if (log_file_name.empty()) - { - // Sanity check: File path should also be empty if file name is. If not, - // this would be a problem in epee's discovery of current process's file - // path. - if (! default_log.empty()) - { - fail_msg_writer() << sw::tr("unexpected empty log file name in presence of non-empty file path"); - return false; - } - // epee didn't find path to executable from argv[0], so use this default file name. - log_file_name = "monero-wallet-cli.log"; - // The full path will use cwd because epee also returned an empty default log folder. - } - default_log /= log_file_name; - - command_line::add_arg(desc_params, arg_log_file, default_log.string()); command_line::add_arg(desc_params, arg_restore_deterministic_wallet ); command_line::add_arg(desc_params, arg_non_deterministic ); command_line::add_arg(desc_params, arg_electrum_seed ); - command_line::add_arg(desc_params, arg_testnet); - command_line::add_arg(desc_params, arg_restricted); command_line::add_arg(desc_params, arg_trusted_daemon); command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version); command_line::add_arg(desc_params, arg_restore_height); - tools::wallet_rpc_server::init_options(desc_params); po::positional_options_description positional_options; positional_options.add(arg_command.name, -1); - i18n_set_language("translations", "monero", lang); + const auto vm = wallet_args::main( + argc, argv, + "monero-wallet-cli [--wallet-file=|--generate-new-wallet=] []", + desc_params, + positional_options + ); - po::options_description desc_all; - desc_all.add(desc_general).add(desc_params); - cryptonote::simple_wallet w; - po::variables_map vm; - bool r = command_line::handle_error_helper(desc_all, [&]() + if (!vm) { - po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); - - if (command_line::get_arg(vm, command_line::arg_help)) - { - success_msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - success_msg_writer() << sw::tr("Usage:") << " monero-wallet-cli [--wallet-file=|--generate-new-wallet=] [--daemon-address=:] []"; - success_msg_writer() << desc_all; - return false; - } - else if (command_line::get_arg(vm, command_line::arg_version)) - { - success_msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - return false; - } - - auto parser = po::command_line_parser(argc, argv).options(desc_params).positional(positional_options); - po::store(parser.run(), vm); - po::notify(vm); - return true; - }); - if (!r) - return 0; - - // log_file_path - // default: < argv[0] directory >/monero-wallet-cli.log - // so if ran as "monero-wallet-cli" (no path), log file will be in cwd - // - // if log-file argument given: - // absolute path - // relative path: relative to cwd - - // Set log file - bf::path log_file_path {bf::absolute(command_line::get_arg(vm, arg_log_file))}; - - // Set up logging options - int log_level = LOG_LEVEL_2; - log_space::get_set_log_detalisation_level(true, log_level); - //log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_file_path.filename().string().c_str(), - log_file_path.parent_path().string().c_str(), - LOG_LEVEL_4 - ); - - if(command_line::has_arg(vm, arg_max_concurrency)) - tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); - - message_writer(epee::log_space::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - - if(command_line::has_arg(vm, arg_log_level)) - log_level = command_line::get_arg(vm, arg_log_level); - LOG_PRINT_L0("Setting log level = " << log_level); - LOG_PRINT_L0(sw::tr("default_log: ") << default_log.string()); - message_writer(epee::log_space::console_color_white, true) << boost::format(sw::tr("Logging at log level %d to %s")) % - log_level % log_file_path.string(); - log_space::get_set_log_detalisation_level(true, log_level); - - if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) - { - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); - //runs wallet with rpc interface - if(!command_line::has_arg(vm, arg_wallet_file) ) - { - LOG_ERROR(sw::tr("Wallet file not set.")); - return 1; - } - if(!command_line::has_arg(vm, arg_daemon_address) ) - { - LOG_ERROR(sw::tr("Daemon address not set.")); - return 1; - } + return 1; + } - bool testnet = command_line::get_arg(vm, arg_testnet); - bool restricted = command_line::get_arg(vm, arg_restricted); - std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); - - tools::password_container pwd_container(wallet_file.empty()); - if (!cryptonote::simple_wallet::get_password(vm, false, pwd_container)) - return 1; - std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); - std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); - int daemon_port = command_line::get_arg(vm, arg_daemon_port); - if (daemon_host.empty()) - daemon_host = "localhost"; - if (!daemon_port) - daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; - if (daemon_address.empty()) - daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - - std::string password; - const std::string gfj = command_line::get_arg(vm, arg_generate_from_json); - if (!gfj.empty()) { - if (!w.generate_from_json(vm, wallet_file, password)) - return 1; - } - else { - password = pwd_container.password(); - } + cryptonote::simple_wallet w; + const bool r = w.init(*vm); + CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet")); - tools::wallet2 wal(testnet,restricted); - bool quit = false; - tools::signal_handler::install([&wal, &quit](int) { - quit = true; - wal.stop(); - }); - try - { - LOG_PRINT_L0(sw::tr("Loading wallet...")); - wal.load(wallet_file, password); - wal.init(daemon_address); - wal.refresh(); - // if we ^C during potentially length load/refresh, there's no server loop yet - if (quit) - { - LOG_PRINT_L0(sw::tr("Storing wallet...")); - wal.store(); - LOG_PRINT_GREEN(sw::tr("Stored ok"), LOG_LEVEL_0); - return 1; - } - LOG_PRINT_GREEN(sw::tr("Loaded ok"), LOG_LEVEL_0); - } - catch (const std::exception& e) - { - LOG_ERROR(sw::tr("Wallet initialization failed: ") << e.what()); - return 1; - } - tools::wallet_rpc_server wrpc(wal); - bool r = wrpc.init(vm); - CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet rpc server")); - tools::signal_handler::install([&wrpc, &wal](int) { - wrpc.send_stop_signal(); - }); - LOG_PRINT_L0(sw::tr("Starting wallet rpc server")); - wrpc.run(); - LOG_PRINT_L0(sw::tr("Stopped wallet rpc server")); - try - { - LOG_PRINT_L0(sw::tr("Storing wallet...")); - wal.store(); - LOG_PRINT_GREEN(sw::tr("Stored ok"), LOG_LEVEL_0); - } - catch (const std::exception& e) - { - LOG_ERROR(sw::tr("Failed to store wallet: ") << e.what()); - return 1; - } - }else + std::vector command = command_line::get_arg(*vm, arg_command); + if (!command.empty()) { - //runs wallet with console interface - r = w.init(vm); - CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet")); - - std::vector command = command_line::get_arg(vm, arg_command); - if (!command.empty()) - { - w.process_command(command); - w.stop(); - w.deinit(); - } - else - { - tools::signal_handler::install([&w](int type) { + w.process_command(command); + w.stop(); + w.deinit(); + } + else + { + tools::signal_handler::install([&w](int type) { #ifdef WIN32 - if (type == CTRL_C_EVENT) + if (type == CTRL_C_EVENT) #else - if (type == SIGINT) + if (type == SIGINT) #endif - { - // if we're pressing ^C when refreshing, just stop refreshing - w.interrupt(); - } - else - { - w.stop(); - } - }); - w.run(); + { + // if we're pressing ^C when refreshing, just stop refreshing + w.interrupt(); + } + else + { + w.stop(); + } + }); + w.run(); - w.deinit(); - } + w.deinit(); } return 0; //CATCH_ENTRY_L0("main", 1); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index fcc77ff69..4fe1b0417 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -37,13 +37,14 @@ #include +#include #include #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_basic_impl.h" #include "wallet/wallet2.h" #include "console_handler.h" -#include "password_container.h" +#include "wallet/password_container.h" #include "crypto/crypto.h" // for definition of crypto::secret_key /*! @@ -58,7 +59,6 @@ namespace cryptonote class simple_wallet : public tools::i_wallet2_callback { public: - static bool get_password(const boost::program_options::variables_map& vm, bool allow_entry, tools::password_container &pwd_container); static const char *tr(const char *str) { return i18n_translate(str, "cryptonote::simple_wallet"); } public: @@ -70,7 +70,6 @@ namespace cryptonote bool run(); void stop(); void interrupt(); - bool generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password); //wallet *create_wallet(); bool process_command(const std::vector &args); @@ -82,13 +81,11 @@ namespace cryptonote void wallet_idle_thread(); - bool new_wallet(const std::string &wallet_file, const std::string& password, const crypto::secret_key& recovery_key, - bool recover, bool two_random, bool testnet, const std::string &old_language); - bool new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, - const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool testnet); - bool new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, - const crypto::secret_key& viewkey, bool testnet); - bool open_wallet(const std::string &wallet_file, const std::string& password, bool testnet); + bool new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, + bool recover, bool two_random, const std::string &old_language); + bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, + const boost::optional& spendkey, const crypto::secret_key& viewkey); + bool open_wallet(const boost::program_options::variables_map& vm); bool close_wallet(); bool viewkey(const std::vector &args = std::vector()); @@ -256,10 +253,6 @@ namespace cryptonote bool m_restoring; // are we restoring, by whatever method? uint64_t m_restore_height; // optional - std::string m_daemon_address; - std::string m_daemon_host; - int m_daemon_port; - epee::console_handlers_binder m_cmd_binder; std::unique_ptr m_wallet; diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 4f82b3c82..e287d9927 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -31,7 +31,9 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(wallet_sources + password_container.cpp wallet2.cpp + wallet_args.cpp wallet_rpc_server.cpp api/wallet.cpp api/wallet_manager.cpp @@ -45,7 +47,9 @@ set(wallet_api_headers set(wallet_private_headers + password_container.h wallet2.h + wallet_args.h wallet_errors.h wallet_rpc_server.h wallet_rpc_server_commands_defs.h @@ -77,6 +81,41 @@ target_link_libraries(wallet PRIVATE ${EXTRA_LIBRARIES}) +set(wallet_rpc_sources + wallet_rpc_server.cpp) + +set(wallet_rpc_headers) + +set(wallet_rpc_private_headers + wallet_rpc_server.h) + +monero_private_headers(wallet_rpc_server + ${wallet_rpc_private_headers}) +monero_add_executable(wallet_rpc_server + ${wallet_rpc_sources} + ${wallet_rpc_headers} + ${wallet_rpc_private_headers}) + +target_link_libraries(wallet_rpc_server + PRIVATE + wallet + rpc + cryptonote_core + crypto + common + ${Boost_CHRONO_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) +add_dependencies(wallet_rpc_server version) +set_property(TARGET wallet_rpc_server + PROPERTY + OUTPUT_NAME "monero-wallet-rpc") +install(TARGETS wallet_rpc_server DESTINATION bin) + + # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct) diff --git a/src/wallet/password_container.cpp b/src/wallet/password_container.cpp new file mode 100644 index 000000000..480d132e7 --- /dev/null +++ b/src/wallet/password_container.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2014-2016, 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 + +#include "password_container.h" + +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#endif + +namespace tools +{ + namespace + { + bool is_cin_tty(); + } + // deleted via private member + password_container::password_container() + : m_empty(true),m_verify(true) + { + + } + password_container::password_container(bool verify) + : m_empty(true),m_verify(verify) + { + + } + + password_container::password_container(std::string&& password) + : m_empty(false) + , m_password(std::move(password)) + , m_verify(false) + { + + } + + + password_container::password_container(password_container&& rhs) + : m_empty(std::move(rhs.m_empty)) + , m_password(std::move(rhs.m_password)) + , m_verify(std::move(rhs.m_verify)) + { + } + password_container::~password_container() + { + clear(); + } + + void password_container::clear() + { + if (0 < m_password.capacity()) + { + m_password.replace(0, m_password.capacity(), m_password.capacity(), '\0'); + m_password.resize(0); + } + m_empty = true; + } + + bool password_container::read_password(const char *message) + { + clear(); + + bool r; + if (is_cin_tty()) + { + r = read_from_tty_double_check(message); + } + else + { + r = read_from_file(); + } + + if (r) + { + m_empty = false; + } + else + { + clear(); + } + + return r; + } + + bool password_container::read_from_file() + { + m_password.reserve(max_password_size); + for (size_t i = 0; i < max_password_size; ++i) + { + char ch = static_cast(std::cin.get()); + if (std::cin.eof() || ch == '\n' || ch == '\r') + { + break; + } + else if (std::cin.fail()) + { + return false; + } + else + { + m_password.push_back(ch); + } + } + + return true; + } + +bool password_container::read_from_tty_double_check(const char *message) { + std::string pass1; + std::string pass2; + bool match=false; + bool doNotVerifyEntry=false; + do{ + if (message) + std::cout << message <<": "; + if (!password_container::read_from_tty(pass1)) + return false; + if (m_verify==true){//double check password; + if (message) + std::cout << message << ": "; + if (!password_container::read_from_tty(pass2)) + return false; + if(pass1!=pass2){ //new password entered did not match + + std::cout << "Passwords do not match" << std::endl; + pass1=""; + pass2=""; + match=false; + } + else{//new password matches + match=true; + } + } + else + doNotVerifyEntry=true; //do not verify + //No need to verify password entered at this point in the code + + }while(match==false && doNotVerifyEntry==false); + + m_password=pass1; + return true; + } + + +#if defined(_WIN32) + + namespace + { + bool is_cin_tty() + { + return 0 != _isatty(_fileno(stdin)); + } + } + + bool password_container::read_from_tty(std::string & pass) + { + const char BACKSPACE = 8; + + HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE); + + DWORD mode_old; + ::GetConsoleMode(h_cin, &mode_old); + DWORD mode_new = mode_old & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); + ::SetConsoleMode(h_cin, mode_new); + + bool r = true; + pass.reserve(max_password_size); + while (pass.size() < max_password_size) + { + DWORD read; + char ch; + r = (TRUE == ::ReadConsoleA(h_cin, &ch, 1, &read, NULL)); + r &= (1 == read); + if (!r) + { + break; + } + else if (ch == '\n' || ch == '\r') + { + std::cout << std::endl; + break; + } + else if (ch == BACKSPACE) + { + if (!pass.empty()) + { + pass.back() = '\0'; + pass.resize(pass.size() - 1); + std::cout << "\b \b"; + } + } + else + { + pass.push_back(ch); + std::cout << '*'; + } + } + + ::SetConsoleMode(h_cin, mode_old); + + return r; + } + +#else + + namespace + { + bool is_cin_tty() + { + return 0 != isatty(fileno(stdin)); + } + + int getch() + { + struct termios tty_old; + tcgetattr(STDIN_FILENO, &tty_old); + + struct termios tty_new; + tty_new = tty_old; + tty_new.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &tty_new); + + int ch = getchar(); + + tcsetattr(STDIN_FILENO, TCSANOW, &tty_old); + + return ch; + } + } + bool password_container::read_from_tty(std::string &aPass) + { + const char BACKSPACE = 127; + + aPass.reserve(max_password_size); + while (aPass.size() < max_password_size) + { + int ch = getch(); + if (EOF == ch) + { + return false; + } + else if (ch == '\n' || ch == '\r') + { + std::cout << std::endl; + break; + } + else if (ch == BACKSPACE) + { + if (!aPass.empty()) + { + aPass.back() = '\0'; + aPass.resize(aPass.size() - 1); + std::cout << "\b \b"; + } + } + else + { + aPass.push_back(ch); + std::cout << '*'; + } + } + + return true; + } + +#endif +} diff --git a/src/wallet/password_container.h b/src/wallet/password_container.h new file mode 100644 index 000000000..62f43aa37 --- /dev/null +++ b/src/wallet/password_container.h @@ -0,0 +1,64 @@ +// Copyright (c) 2014-2016, 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 + +#pragma once + +#include +#include + +namespace tools +{ + class password_container + { + public: + static const size_t max_password_size = 1024; + password_container(bool verify); + password_container(password_container&& rhs); + password_container(std::string&& password); + ~password_container(); + + void clear(); + bool empty() const { return m_empty; } + const std::string& password() const { return m_password; } + void password(std::string&& val) { m_password = std::move(val); m_empty = false; } + bool read_password(const char *message = "password"); + + private: + //delete constructor with no parameters + password_container(); + bool read_from_file(); + bool read_from_tty(std::string & pass); + bool read_from_tty_double_check(const char *message); + + bool m_empty; + std::string m_password; + bool m_verify; + }; +} diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ac8802ca4..1ed44ee98 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -32,23 +32,27 @@ #include #include #include - +#include +#include #include #include "include_base_utils.h" using namespace epee; #include "cryptonote_config.h" #include "wallet2.h" +#include "wallet2_api.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "rpc/core_rpc_server_commands_defs.h" #include "misc_language.h" #include "cryptonote_core/cryptonote_basic_impl.h" #include "common/boost_serialization_helper.h" +#include "common/command_line.h" #include "profile_tools.h" #include "crypto/crypto.h" #include "serialization/binary_utils.h" #include "cryptonote_protocol/blobdatatype.h" #include "mnemonics/electrum-words.h" +#include "common/i18n.h" #include "common/dns_utils.h" #include "common/util.h" #include "rapidjson/document.h" @@ -56,6 +60,7 @@ using namespace epee; #include "rapidjson/stringbuffer.h" #include "common/json_util.h" #include "common/base58.h" +#include "common/scoped_message_writer.h" #include "ringct/rctSigs.h" extern "C" @@ -92,6 +97,17 @@ using namespace cryptonote; namespace { +// Create on-demand to prevent static initialization order fiasco issues. +struct options { + const command_line::arg_descriptor daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at :"), ""}; + const command_line::arg_descriptor daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host instead of localhost"), ""}; + const command_line::arg_descriptor password = {"password", tools::wallet2::tr("Wallet password"), "", true}; + const command_line::arg_descriptor password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true}; + const command_line::arg_descriptor daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port instead of 18081"), 0}; + const command_line::arg_descriptor testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; + const command_line::arg_descriptor restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false}; +}; + void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { keys_file = file_path; @@ -117,6 +133,279 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui return calculate_fee(fee_per_kb, blob.size(), fee_multiplier); } +std::unique_ptr make_basic(const boost::program_options::variables_map& vm, const options& opts) +{ + const bool testnet = command_line::get_arg(vm, opts.testnet); + const bool restricted = command_line::get_arg(vm, opts.restricted); + + auto daemon_address = command_line::get_arg(vm, opts.daemon_address); + auto daemon_host = command_line::get_arg(vm, opts.daemon_host); + auto daemon_port = command_line::get_arg(vm, opts.daemon_port); + + if (!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port) + { + tools::fail_msg_writer() << tools::wallet2::tr("can't specify daemon host or port more than once"); + return nullptr; + } + + if (daemon_host.empty()) + daemon_host = "localhost"; + + if (!daemon_port) + { + daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; + } + + if (daemon_address.empty()) + daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); + + std::unique_ptr wallet(new tools::wallet2(testnet, restricted)); + wallet->init(daemon_address); + return wallet; +} + +boost::optional get_password(const boost::program_options::variables_map& vm, const options& opts, const bool verify) +{ + if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file)) + { + tools::fail_msg_writer() << tools::wallet2::tr("can't specify more than one of --password and --password-file"); + return boost::none; + } + + if (command_line::has_arg(vm, opts.password)) + { + tools::password_container pwd(false); + pwd.password(command_line::get_arg(vm, opts.password)); + return {std::move(pwd)}; + } + + if (command_line::has_arg(vm, opts.password_file)) + { + std::string password; + bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file), + password); + if (!r) + { + tools::fail_msg_writer() << tools::wallet2::tr("the password file specified could not be read"); + return boost::none; + } + + // Remove line breaks the user might have inserted + password.erase(std::remove(password.begin() - 1, password.end(), '\n'), password.end()); + password.erase(std::remove(password.end() - 1, password.end(), '\r'), password.end()); + return {tools::password_container(std::move(password))}; + } + + //vm is already part of the password container class. just need to check vm for an already existing wallet + //here need to pass in variable map. This will indicate if the wallet already exists to the read password function + tools::password_container pwd(verify); + if (pwd.read_password()) + { + return {std::move(pwd)}; + } + + tools::fail_msg_writer() << tools::wallet2::tr("failed to read wallet password"); + return boost::none; +} + +std::unique_ptr generate_from_json(const std::string& json_file, bool testnet, bool restricted) +{ + /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return + false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly + fails. This large wrapper is for the use of that macro */ + std::unique_ptr wallet; + const auto do_generate = [&]() -> bool { + std::string buf; + if (!epee::file_io_utils::load_file_to_string(json_file, buf)) { + tools::fail_msg_writer() << tools::wallet2::tr("Failed to load file ") << json_file; + return false; + } + + rapidjson::Document json; + if (json.Parse(buf.c_str()).HasParseError()) { + tools::fail_msg_writer() << tools::wallet2::tr("Failed to parse JSON"); + return false; + } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0); + const int current_version = 1; + if (field_version > current_version) { + tools::fail_msg_writer() << boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version; + return false; + } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string()); + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0); + const bool recover = field_scan_from_height_found; + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string()); + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, viewkey, std::string, String, false, std::string()); + crypto::secret_key viewkey; + if (field_viewkey_found) + { + cryptonote::blobdata viewkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data)) + { + tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key"); + return false; + } + viewkey = *reinterpret_cast(viewkey_data.data()); + crypto::public_key pkey; + if (!crypto::secret_key_to_public_key(viewkey, pkey)) { + tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key"); + return false; + } + } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, spendkey, std::string, String, false, std::string()); + crypto::secret_key spendkey; + if (field_spendkey_found) + { + cryptonote::blobdata spendkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data)) + { + tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key"); + return false; + } + spendkey = *reinterpret_cast(spendkey_data.data()); + crypto::public_key pkey; + if (!crypto::secret_key_to_public_key(spendkey, pkey)) { + tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key"); + return false; + } + } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed, std::string, String, false, std::string()); + std::string old_language; + crypto::secret_key recovery_key; + bool restore_deterministic_wallet = false; + if (field_seed_found) + { + if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language)) + { + tools::fail_msg_writer() << tools::wallet2::tr("Electrum-style word list failed verification"); + return false; + } + restore_deterministic_wallet = true; + } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string()); + + // compatibility checks + if (!field_seed_found && !field_viewkey_found) + { + tools::fail_msg_writer() << tools::wallet2::tr("At least one of Electrum-style word list and private view key must be specified"); + return false; + } + if (field_seed_found && (field_viewkey_found || field_spendkey_found)) + { + tools::fail_msg_writer() << tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"); + return false; + } + + // if an address was given, we check keys against it, and deduce the spend + // public key if it was not given + if (field_address_found) + { + cryptonote::account_public_address address; + bool has_payment_id; + crypto::hash8 new_payment_id; + if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, testnet, field_address)) + { + tools::fail_msg_writer() << tools::wallet2::tr("invalid address"); + return false; + } + if (field_viewkey_found) + { + crypto::public_key pkey; + if (!crypto::secret_key_to_public_key(viewkey, pkey)) { + tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key"); + return false; + } + if (address.m_view_public_key != pkey) { + tools::fail_msg_writer() << tools::wallet2::tr("view key does not match standard address"); + return false; + } + } + if (field_spendkey_found) + { + crypto::public_key pkey; + if (!crypto::secret_key_to_public_key(spendkey, pkey)) { + tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key"); + return false; + } + if (address.m_spend_public_key != pkey) { + tools::fail_msg_writer() << tools::wallet2::tr("spend key does not match standard address"); + return false; + } + } + } + + const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) || + crypto::ElectrumWords::get_is_old_style_seed(field_seed)); + if (deprecated_wallet) { + tools::fail_msg_writer() << tools::wallet2::tr("Cannot create deprecated wallets from JSON"); + return false; + } + + wallet.reset(new tools::wallet2(testnet, restricted)); + wallet->set_refresh_from_block_height(field_scan_from_height); + + try + { + if (!field_seed.empty()) + { + wallet->generate(field_filename, field_password, recovery_key, recover, false); + } + else + { + cryptonote::account_public_address address; + if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) { + tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key"); + return false; + } + + if (field_spendkey.empty()) + { + // if we have an addres but no spend key, we can deduce the spend public key + // from the address + if (field_address_found) + { + cryptonote::account_public_address address2; + bool has_payment_id; + crypto::hash8 new_payment_id; + get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address); + address.m_spend_public_key = address2.m_spend_public_key; + } + wallet->generate(field_filename, field_password, address, viewkey); + } + else + { + if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) { + tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key"); + return false; + } + wallet->generate(field_filename, field_password, address, spendkey, viewkey); + } + } + } + catch (const std::exception& e) + { + tools::fail_msg_writer() << tools::wallet2::tr("failed to generate new wallet: ") << e.what(); + return false; + } + return true; + }; + + if (do_generate()) + { + return wallet; + } + return nullptr; +} + } //namespace namespace tools @@ -124,6 +413,59 @@ namespace tools // for now, limit to 30 attempts. TODO: discuss a good number to limit to. const size_t MAX_SPLIT_ATTEMPTS = 30; +const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); } + +bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm) +{ + return command_line::get_arg(vm, options().testnet); +} + +void wallet2::init_options(boost::program_options::options_description& desc_params) +{ + const options opts{}; + command_line::add_arg(desc_params, opts.daemon_address); + command_line::add_arg(desc_params, opts.daemon_host); + command_line::add_arg(desc_params, opts.password); + command_line::add_arg(desc_params, opts.password_file); + command_line::add_arg(desc_params, opts.daemon_port); + command_line::add_arg(desc_params, opts.testnet); + command_line::add_arg(desc_params, opts.restricted); +} + +std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file) +{ + const options opts{}; + return generate_from_json(json_file, command_line::get_arg(vm, opts.testnet), command_line::get_arg(vm, opts.restricted)); +} + +std::pair, password_container> wallet2::make_from_file( + const boost::program_options::variables_map& vm, const std::string& wallet_file) +{ + const options opts{}; + auto pwd = get_password(vm, opts, false); + if (!pwd) + { + return {nullptr, password_container(false)}; + } + auto wallet = make_basic(vm, opts); + if (wallet) + { + wallet->load(wallet_file, pwd->password()); + } + return {std::move(wallet), std::move(*pwd)}; +} + +std::pair, password_container> wallet2::make_new(const boost::program_options::variables_map& vm) +{ + const options opts{}; + auto pwd = get_password(vm, opts, true); + if (!pwd) + { + return {nullptr, password_container(false)}; + } + return {make_basic(vm, opts), std::move(*pwd)}; +} + //---------------------------------------------------------------------------------------------------- void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3c4b1015f..d42385caf 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -32,6 +32,9 @@ #include #include + +#include +#include #include #include #include @@ -51,6 +54,7 @@ #include "ringct/rctOps.h" #include "wallet_errors.h" +#include "password_container.h" #include #define WALLET_RCP_CONNECTION_TIMEOUT 200000 @@ -95,6 +99,21 @@ namespace tools wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true) {} public: + static const char* tr(const char* str);// { return i18n_translate(str, "cryptonote::simple_wallet"); } + + static bool has_testnet_option(const boost::program_options::variables_map& vm); + static void init_options(boost::program_options::options_description& desc_params); + + //! Uses stdin and stdout. Returns a wallet2 if no errors. + static std::unique_ptr make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file); + + //! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors. + static std::pair, password_container> + make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file); + + //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors. + static std::pair, password_container> make_new(const boost::program_options::variables_map& vm); + wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_restricted(restricted), is_old_file_format(false) {} struct transfer_details { diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp new file mode 100644 index 000000000..f7eec8cfc --- /dev/null +++ b/src/wallet/wallet_args.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2014-2016, 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. +#include "wallet/wallet_args.h" + +#include +#include +#include "common/i18n.h" +#include "common/scoped_message_writer.h" +#include "common/util.h" +#include "misc_log_ex.h" +#include "string_tools.h" +#include "version.h" + +#if defined(WIN32) +#include +#endif + +// workaround for a suspected bug in pthread/kernel on MacOS X +#ifdef __APPLE__ +#define DEFAULT_MAX_CONCURRENCY 1 +#else +#define DEFAULT_MAX_CONCURRENCY 0 +#endif + + +namespace wallet_args +{ + // Create on-demand to prevent static initialization order fiasco issues. + command_line::arg_descriptor arg_generate_from_json() + { + return {"generate-from-json", wallet_args::tr("Generate wallet from JSON format file"), ""}; + } + command_line::arg_descriptor arg_wallet_file() + { + return {"wallet-file", wallet_args::tr("Use wallet "), ""}; + } + + const char* tr(const char* str) + { + return i18n_translate(str, "wallet_args"); + } + + boost::optional main( + int argc, char** argv, + const char* const usage, + boost::program_options::options_description desc_params, + const boost::program_options::positional_options_description& positional_options) + + { + namespace bf = boost::filesystem; + namespace po = boost::program_options; +#ifdef WIN32 + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + + const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; + const command_line::arg_descriptor arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY}; + const command_line::arg_descriptor arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""}; + + + std::string lang = i18n_get_language(); + tools::sanitize_locale(); + tools::set_strict_default_file_permissions(true); + + epee::string_tools::set_module_name_and_folder(argv[0]); + + po::options_description desc_general(wallet_args::tr("General options")); + command_line::add_arg(desc_general, command_line::arg_help); + command_line::add_arg(desc_general, command_line::arg_version); + + + bf::path default_log {epee::log_space::log_singletone::get_default_log_folder()}; + std::string log_file_name = epee::log_space::log_singletone::get_default_log_file(); + if (log_file_name.empty()) + { + // Sanity check: File path should also be empty if file name is. If not, + // this would be a problem in epee's discovery of current process's file + // path. + if (! default_log.empty()) + { + tools::fail_msg_writer() << wallet_args::tr("unexpected empty log file name in presence of non-empty file path"); + return boost::none; + } + // epee didn't find path to executable from argv[0], so use this default file name. + log_file_name = "monero-wallet-cli.log"; + // The full path will use cwd because epee also returned an empty default log folder. + } + default_log /= log_file_name; + + command_line::add_arg(desc_params, arg_log_file, default_log.string()); + command_line::add_arg(desc_params, arg_log_level); + command_line::add_arg(desc_params, arg_max_concurrency); + + i18n_set_language("translations", "monero", lang); + + po::options_description desc_all; + desc_all.add(desc_general).add(desc_params); + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_all, [&]() + { + po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage; + tools::msg_writer() << desc_all; + return false; + } + else if (command_line::get_arg(vm, command_line::arg_version)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + return false; + } + + auto parser = po::command_line_parser(argc, argv).options(desc_params).positional(positional_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (!r) + return boost::none; + + // log_file_path + // default: < argv[0] directory >/monero-wallet-cli.log + // so if ran as "monero-wallet-cli" (no path), log file will be in cwd + // + // if log-file argument given: + // absolute path + // relative path: relative to cwd + + // Set log file + bf::path log_file_path {bf::absolute(command_line::get_arg(vm, arg_log_file))}; + + // Set up logging options + int log_level = LOG_LEVEL_2; + epee::log_space::get_set_log_detalisation_level(true, log_level); + //epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0); + epee::log_space::log_singletone::add_logger(LOGGER_FILE, + log_file_path.filename().string().c_str(), + log_file_path.parent_path().string().c_str(), + LOG_LEVEL_4 + ); + + if(command_line::has_arg(vm, arg_max_concurrency)) + tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); + + tools::scoped_message_writer(epee::log_space::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + + if(command_line::has_arg(vm, arg_log_level)) + log_level = command_line::get_arg(vm, arg_log_level); + LOG_PRINT_L0("Setting log level = " << log_level); + LOG_PRINT_L0(wallet_args::tr("default_log: ") << default_log.string()); + tools::scoped_message_writer(epee::log_space::console_color_white, true) << boost::format(wallet_args::tr("Logging at log level %d to %s")) % + log_level % log_file_path.string(); + epee::log_space::get_set_log_detalisation_level(true, log_level); + + return {std::move(vm)}; + } +} diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h new file mode 100644 index 000000000..17446abf3 --- /dev/null +++ b/src/wallet/wallet_args.h @@ -0,0 +1,53 @@ +// Copyright (c) 2014-2016, 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. +#include +#include +#include +#include + +#include "common/command_line.h" + +namespace wallet_args +{ + command_line::arg_descriptor arg_generate_from_json(); + command_line::arg_descriptor arg_wallet_file(); + + const char* tr(const char* str); + + /*! Processes command line arguments (`argc` and `argv`) using `desc_params` + and `positional_options`, while adding parameters for log files and + concurrency. Log file and concurrency arguments are handled, along with basic + global init for the wallet process. + + \return The list of parsed options, iff there are no errors.*/ + boost::optional main( + int argc, char** argv, + const char* const usage, + boost::program_options::options_description desc_params, + const boost::program_options::positional_options_description& positional_options); +} diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index faa40e166..92ad65c5b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -27,12 +27,14 @@ // 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 - +#include #include "include_base_utils.h" using namespace epee; #include "wallet_rpc_server.h" +#include "wallet/wallet_args.h" #include "common/command_line.h" +#include "common/i18n.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/account.h" #include "wallet_rpc_server_commands_defs.h" @@ -40,19 +42,20 @@ using namespace epee; #include "string_tools.h" #include "crypto/hash.h" -namespace tools +namespace { - //----------------------------------------------------------------------------------- - const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = {"rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", "", true}; - const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"}; - const command_line::arg_descriptor wallet_rpc_server::arg_user_agent = {"user-agent", "Restrict RPC to clients using this user agent", ""}; + const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; + const command_line::arg_descriptor arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"}; + const command_line::arg_descriptor arg_user_agent = {"user-agent", "Restrict RPC to clients using this user agent", ""}; +} - void wallet_rpc_server::init_options(boost::program_options::options_description& desc) +namespace tools +{ + const char* wallet_rpc_server::tr(const char* str) { - command_line::add_arg(desc, arg_rpc_bind_ip); - command_line::add_arg(desc, arg_rpc_bind_port); - command_line::add_arg(desc, arg_user_agent); + return i18n_translate(str, "tools::wallet_rpc_server"); } + //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w) {} @@ -1070,3 +1073,107 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ } +int main(int argc, char** argv) { + namespace po = boost::program_options; + + const auto arg_wallet_file = wallet_args::arg_wallet_file(); + const auto arg_from_json = wallet_args::arg_generate_from_json(); + + po::options_description desc_params(wallet_args::tr("Wallet options")); + tools::wallet2::init_options(desc_params); + command_line::add_arg(desc_params, arg_rpc_bind_ip); + command_line::add_arg(desc_params, arg_rpc_bind_port); + command_line::add_arg(desc_params, arg_user_agent); + command_line::add_arg(desc_params, arg_wallet_file); + command_line::add_arg(desc_params, arg_from_json); + + const auto vm = wallet_args::main( + argc, argv, + "monero-wallet-rpc [--wallet-file=|--generate-from-json=] [--rpc-bind-port=]", + desc_params, + po::positional_options_description() + ); + if (!vm) + { + return 1; + } + + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); + + std::unique_ptr wal; + try + { + const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); + const auto from_json = command_line::get_arg(*vm, arg_from_json); + + if(!wallet_file.empty() && !from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); + return 1; + } + + if (wallet_file.empty() && from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json")); + return 1; + } + + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); + if(!wallet_file.empty()) + { + wal = tools::wallet2::make_from_file(*vm, wallet_file).first; + } + else + { + wal = tools::wallet2::make_from_json(*vm, from_json); + } + if (!wal) + { + return 1; + } + + bool quit = false; + tools::signal_handler::install([&wal, &quit](int) { + assert(wal); + quit = true; + wal->stop(); + }); + + wal->refresh(); + // if we ^C during potentially length load/refresh, there's no server loop yet + if (quit) + { + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet...")); + wal->store(); + LOG_PRINT_GREEN(tools::wallet_rpc_server::tr("Stored ok"), LOG_LEVEL_0); + return 1; + } + LOG_PRINT_GREEN(tools::wallet_rpc_server::tr("Loaded ok"), LOG_LEVEL_0); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); + return 1; + } + tools::wallet_rpc_server wrpc(*wal); + bool r = wrpc.init(*vm); + CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet rpc server")); + tools::signal_handler::install([&wrpc, &wal](int) { + wrpc.send_stop_signal(); + }); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet rpc server")); + wrpc.run(); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet rpc server")); + try + { + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet...")); + wal->store(); + LOG_PRINT_GREEN(tools::wallet_rpc_server::tr("Stored ok"), LOG_LEVEL_0); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to store wallet: ") << e.what()); + return 1; + } + return 0; +} diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b3e95c18a..4eceb1d55 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -35,7 +35,6 @@ #include "net/http_server_impl_base.h" #include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" -#include "common/command_line.h" namespace tools { /************************************************************************/ @@ -46,14 +45,10 @@ namespace tools public: typedef epee::net_utils::connection_context_base connection_context; - wallet_rpc_server(wallet2& cr); - - const static command_line::arg_descriptor arg_rpc_bind_port; - const static command_line::arg_descriptor arg_rpc_bind_ip; - const static command_line::arg_descriptor arg_user_agent; + static const char* tr(const char* str); + wallet_rpc_server(wallet2& cr); - static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); bool run(); private: -- cgit v1.2.3