aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
authorLee Clagett <code@leeclagett.com>2016-11-08 22:55:41 -0500
committerLee Clagett <code@leeclagett.com>2016-11-10 16:39:27 -0500
commit358e068e878892eb3cc0f333215708390e51f0c3 (patch)
tree6fda7493c79996da048bac8f969574e4715c4ced /src/wallet
parentMerge pull request #1312 (diff)
downloadmonero-358e068e878892eb3cc0f333215708390e51f0c3.tar.xz
Created monero-wallet-rpc, moving functionality from monero-wallet-cli
Diffstat (limited to '')
-rw-r--r--src/wallet/CMakeLists.txt39
-rw-r--r--src/wallet/password_container.cpp (renamed from src/simplewallet/password_container.cpp)0
-rw-r--r--src/wallet/password_container.h (renamed from src/simplewallet/password_container.h)0
-rw-r--r--src/wallet/wallet2.cpp344
-rw-r--r--src/wallet/wallet2.h19
-rw-r--r--src/wallet/wallet_args.cpp185
-rw-r--r--src/wallet/wallet_args.h53
-rw-r--r--src/wallet/wallet_rpc_server.cpp127
-rw-r--r--src/wallet/wallet_rpc_server.h9
9 files changed, 758 insertions, 18 deletions
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/simplewallet/password_container.cpp b/src/wallet/password_container.cpp
index 480d132e7..480d132e7 100644
--- a/src/simplewallet/password_container.cpp
+++ b/src/wallet/password_container.cpp
diff --git a/src/simplewallet/password_container.h b/src/wallet/password_container.h
index 62f43aa37..62f43aa37 100644
--- a/src/simplewallet/password_container.h
+++ b/src/wallet/password_container.h
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 <tuple>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
-
+#include <boost/format.hpp>
+#include <boost/optional/optional.hpp>
#include <boost/utility/value_init.hpp>
#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<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
+ const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
+ const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password"), "", true};
+ const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
+ const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0};
+ const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
+ const command_line::arg_descriptor<bool> 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<tools::wallet2> 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<tools::wallet2> wallet(new tools::wallet2(testnet, restricted));
+ wallet->init(daemon_address);
+ return wallet;
+}
+
+boost::optional<tools::password_container> 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<tools::wallet2> 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<tools::wallet2> 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<const crypto::secret_key*>(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<const crypto::secret_key*>(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> 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<std::unique_ptr<wallet2>, 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<std::unique_ptr<wallet2>, 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 <memory>
#include <boost/archive/binary_iarchive.hpp>
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <atomic>
@@ -51,6 +54,7 @@
#include "ringct/rctOps.h"
#include "wallet_errors.h"
+#include "password_container.h"
#include <iostream>
#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<wallet2> 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<std::unique_ptr<wallet2>, 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<std::unique_ptr<wallet2>, 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 <boost/filesystem/path.hpp>
+#include <boost/format.hpp>
+#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 <crtdbg.h>
+#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<std::string> arg_generate_from_json()
+ {
+ return {"generate-from-json", wallet_args::tr("Generate wallet from JSON format file"), ""};
+ }
+ command_line::arg_descriptor<std::string> arg_wallet_file()
+ {
+ return {"wallet-file", wallet_args::tr("Use wallet <arg>"), ""};
+ }
+
+ const char* tr(const char* str)
+ {
+ return i18n_translate(str, "wallet_args");
+ }
+
+ boost::optional<boost::program_options::variables_map> 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<uint32_t> arg_log_level = {"log-level", "", LOG_LEVEL_0};
+ const command_line::arg_descriptor<uint32_t> 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<std::string> 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 <boost/optional/optional.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/positional_options.hpp>
+#include <boost/program_options/variables_map.hpp>
+
+#include "common/command_line.h"
+
+namespace wallet_args
+{
+ command_line::arg_descriptor<std::string> arg_generate_from_json();
+ command_line::arg_descriptor<std::string> 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<boost::program_options::variables_map> 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 <cstdint>
#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<std::string> 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<std::string> 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<std::string> wallet_rpc_server::arg_user_agent = {"user-agent", "Restrict RPC to clients using this user agent", ""};
+ const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
+ const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"};
+ const command_line::arg_descriptor<std::string> 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=<file>|--generate-from-json=<file>] [--rpc-bind-port=<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<tools::wallet2> 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<std::string> arg_rpc_bind_port;
- const static command_line::arg_descriptor<std::string> arg_rpc_bind_ip;
- const static command_line::arg_descriptor<std::string> 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: