Created monero-wallet-rpc, moving functionality from monero-wallet-cli

This commit is contained in:
Lee Clagett 2016-11-08 22:55:41 -05:00
parent 38727f7d2f
commit 358e068e87
13 changed files with 2475 additions and 1238 deletions

View file

@ -27,14 +27,12 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(simplewallet_sources set(simplewallet_sources
simplewallet.cpp simplewallet.cpp)
password_container.cpp)
set(simplewallet_headers) set(simplewallet_headers)
set(simplewallet_private_headers set(simplewallet_private_headers
simplewallet.h simplewallet.h)
password_container.h)
monero_private_headers(simplewallet monero_private_headers(simplewallet
${simplewallet_private_headers}) ${simplewallet_private_headers})

File diff suppressed because it is too large Load diff

View file

@ -37,13 +37,14 @@
#include <memory> #include <memory>
#include <boost/optional/optional.hpp>
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include "cryptonote_core/account.h" #include "cryptonote_core/account.h"
#include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/cryptonote_basic_impl.h"
#include "wallet/wallet2.h" #include "wallet/wallet2.h"
#include "console_handler.h" #include "console_handler.h"
#include "password_container.h" #include "wallet/password_container.h"
#include "crypto/crypto.h" // for definition of crypto::secret_key #include "crypto/crypto.h" // for definition of crypto::secret_key
/*! /*!
@ -58,7 +59,6 @@ namespace cryptonote
class simple_wallet : public tools::i_wallet2_callback class simple_wallet : public tools::i_wallet2_callback
{ {
public: 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"); } static const char *tr(const char *str) { return i18n_translate(str, "cryptonote::simple_wallet"); }
public: public:
@ -70,7 +70,6 @@ namespace cryptonote
bool run(); bool run();
void stop(); void stop();
void interrupt(); void interrupt();
bool generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password);
//wallet *create_wallet(); //wallet *create_wallet();
bool process_command(const std::vector<std::string> &args); bool process_command(const std::vector<std::string> &args);
@ -82,13 +81,11 @@ namespace cryptonote
void wallet_idle_thread(); void wallet_idle_thread();
bool new_wallet(const std::string &wallet_file, const std::string& password, const crypto::secret_key& recovery_key, bool new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
bool recover, bool two_random, bool testnet, const std::string &old_language); bool recover, bool two_random, const std::string &old_language);
bool new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const crypto::secret_key& spendkey, const crypto::secret_key& viewkey, bool testnet); const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
bool new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, bool open_wallet(const boost::program_options::variables_map& vm);
const crypto::secret_key& viewkey, bool testnet);
bool open_wallet(const std::string &wallet_file, const std::string& password, bool testnet);
bool close_wallet(); bool close_wallet();
bool viewkey(const std::vector<std::string> &args = std::vector<std::string>()); bool viewkey(const std::vector<std::string> &args = std::vector<std::string>());
@ -256,10 +253,6 @@ namespace cryptonote
bool m_restoring; // are we restoring, by whatever method? bool m_restoring; // are we restoring, by whatever method?
uint64_t m_restore_height; // optional 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; epee::console_handlers_binder m_cmd_binder;
std::unique_ptr<tools::wallet2> m_wallet; std::unique_ptr<tools::wallet2> m_wallet;

View file

@ -31,7 +31,9 @@
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(wallet_sources set(wallet_sources
password_container.cpp
wallet2.cpp wallet2.cpp
wallet_args.cpp
wallet_rpc_server.cpp wallet_rpc_server.cpp
api/wallet.cpp api/wallet.cpp
api/wallet_manager.cpp api/wallet_manager.cpp
@ -45,7 +47,9 @@ set(wallet_api_headers
set(wallet_private_headers set(wallet_private_headers
password_container.h
wallet2.h wallet2.h
wallet_args.h
wallet_errors.h wallet_errors.h
wallet_rpc_server.h wallet_rpc_server.h
wallet_rpc_server_commands_defs.h wallet_rpc_server_commands_defs.h
@ -77,6 +81,41 @@ target_link_libraries(wallet
PRIVATE PRIVATE
${EXTRA_LIBRARIES}) ${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 # build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS) if (BUILD_GUI_DEPS)
set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct) set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct)

View file

@ -32,23 +32,27 @@
#include <tuple> #include <tuple>
#include <boost/archive/binary_oarchive.hpp> #include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp> #include <boost/archive/binary_iarchive.hpp>
#include <boost/format.hpp>
#include <boost/optional/optional.hpp>
#include <boost/utility/value_init.hpp> #include <boost/utility/value_init.hpp>
#include "include_base_utils.h" #include "include_base_utils.h"
using namespace epee; using namespace epee;
#include "cryptonote_config.h" #include "cryptonote_config.h"
#include "wallet2.h" #include "wallet2.h"
#include "wallet2_api.h"
#include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "misc_language.h" #include "misc_language.h"
#include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/cryptonote_basic_impl.h"
#include "common/boost_serialization_helper.h" #include "common/boost_serialization_helper.h"
#include "common/command_line.h"
#include "profile_tools.h" #include "profile_tools.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "serialization/binary_utils.h" #include "serialization/binary_utils.h"
#include "cryptonote_protocol/blobdatatype.h" #include "cryptonote_protocol/blobdatatype.h"
#include "mnemonics/electrum-words.h" #include "mnemonics/electrum-words.h"
#include "common/i18n.h"
#include "common/dns_utils.h" #include "common/dns_utils.h"
#include "common/util.h" #include "common/util.h"
#include "rapidjson/document.h" #include "rapidjson/document.h"
@ -56,6 +60,7 @@ using namespace epee;
#include "rapidjson/stringbuffer.h" #include "rapidjson/stringbuffer.h"
#include "common/json_util.h" #include "common/json_util.h"
#include "common/base58.h" #include "common/base58.h"
#include "common/scoped_message_writer.h"
#include "ringct/rctSigs.h" #include "ringct/rctSigs.h"
extern "C" extern "C"
@ -92,6 +97,17 @@ using namespace cryptonote;
namespace 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) void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
{ {
keys_file = file_path; 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); 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
namespace tools namespace tools
@ -124,6 +413,59 @@ namespace tools
// for now, limit to 30 attempts. TODO: discuss a good number to limit to. // for now, limit to 30 attempts. TODO: discuss a good number to limit to.
const size_t MAX_SPLIT_ATTEMPTS = 30; 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) void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit)
{ {

View file

@ -32,6 +32,9 @@
#include <memory> #include <memory>
#include <boost/archive/binary_iarchive.hpp> #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/list.hpp>
#include <boost/serialization/vector.hpp> #include <boost/serialization/vector.hpp>
#include <atomic> #include <atomic>
@ -51,6 +54,7 @@
#include "ringct/rctOps.h" #include "ringct/rctOps.h"
#include "wallet_errors.h" #include "wallet_errors.h"
#include "password_container.h"
#include <iostream> #include <iostream>
#define WALLET_RCP_CONNECTION_TIMEOUT 200000 #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) {} 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: 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) {} 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 struct transfer_details
{ {

185
src/wallet/wallet_args.cpp Normal file
View file

@ -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)};
}
}

53
src/wallet/wallet_args.h Normal file
View file

@ -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);
}

View file

@ -27,12 +27,14 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <cstdint>
#include "include_base_utils.h" #include "include_base_utils.h"
using namespace epee; using namespace epee;
#include "wallet_rpc_server.h" #include "wallet_rpc_server.h"
#include "wallet/wallet_args.h"
#include "common/command_line.h" #include "common/command_line.h"
#include "common/i18n.h"
#include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_format_utils.h"
#include "cryptonote_core/account.h" #include "cryptonote_core/account.h"
#include "wallet_rpc_server_commands_defs.h" #include "wallet_rpc_server_commands_defs.h"
@ -40,19 +42,20 @@ using namespace epee;
#include "string_tools.h" #include "string_tools.h"
#include "crypto/hash.h" #include "crypto/hash.h"
namespace
{
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", ""};
}
namespace tools namespace tools
{ {
//----------------------------------------------------------------------------------- const char* wallet_rpc_server::tr(const char* str)
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", ""};
void wallet_rpc_server::init_options(boost::program_options::options_description& desc)
{ {
command_line::add_arg(desc, arg_rpc_bind_ip); return i18n_translate(str, "tools::wallet_rpc_server");
command_line::add_arg(desc, arg_rpc_bind_port);
command_line::add_arg(desc, arg_user_agent);
} }
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w) 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;
}

View file

@ -35,7 +35,6 @@
#include "net/http_server_impl_base.h" #include "net/http_server_impl_base.h"
#include "wallet_rpc_server_commands_defs.h" #include "wallet_rpc_server_commands_defs.h"
#include "wallet2.h" #include "wallet2.h"
#include "common/command_line.h"
namespace tools namespace tools
{ {
/************************************************************************/ /************************************************************************/
@ -46,14 +45,10 @@ namespace tools
public: public:
typedef epee::net_utils::connection_context_base connection_context; typedef epee::net_utils::connection_context_base connection_context;
static const char* tr(const char* str);
wallet_rpc_server(wallet2& cr); 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 void init_options(boost::program_options::options_description& desc);
bool init(const boost::program_options::variables_map& vm); bool init(const boost::program_options::variables_map& vm);
bool run(); bool run();
private: private:

File diff suppressed because it is too large Load diff