Merge pull request #1316

358e068 Created monero-wallet-rpc, moving functionality from monero-wallet-cli (Lee Clagett)
This commit is contained in:
Riccardo Spagni 2016-11-11 12:48:39 +02:00
commit 6a2bb62827
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
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})

View File

@ -52,25 +52,19 @@
#include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_format_utils.h"
#include "storages/http_abstract_invoke.h" #include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.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 "crypto/crypto.h" // for crypto::secret_key definition
#include "mnemonics/electrum-words.h" #include "mnemonics/electrum-words.h"
#include "rapidjson/document.h" #include "rapidjson/document.h"
#include "common/json_util.h" #include "common/json_util.h"
#include "ringct/rctSigs.h" #include "ringct/rctSigs.h"
#include "wallet/wallet_args.h"
#include <stdexcept> #include <stdexcept>
#if defined(WIN32)
#include <crtdbg.h>
#endif
using namespace std; using namespace std;
using namespace epee; using namespace epee;
using namespace cryptonote; using namespace cryptonote;
using boost::lexical_cast; using boost::lexical_cast;
namespace po = boost::program_options; namespace po = boost::program_options;
namespace bf = boost::filesystem;
typedef cryptonote::simple_wallet sw; typedef cryptonote::simple_wallet sw;
#define EXTENDED_LOGS_FILE "wallet_details.log" #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 KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output 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() \ #define LOCK_IDLE_SCOPE() \
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
@ -111,24 +98,14 @@ enum TransferType {
namespace namespace
{ {
const command_line::arg_descriptor<std::string> arg_wallet_file = {"wallet-file", sw::tr("Use wallet <arg>"), ""}; const auto arg_wallet_file = wallet_args::arg_wallet_file();
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""}; const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_json = {"generate-from-json", sw::tr("Generate wallet from JSON format file"), ""}; const auto arg_generate_from_json = wallet_args::arg_generate_from_json();
const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", sw::tr("Use daemon instance at <host>:<port>"), ""};
const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", sw::tr("Use daemon instance at host <arg> instead of localhost"), ""};
const command_line::arg_descriptor<std::string> arg_password = {"password", sw::tr("Wallet password"), "", true};
const command_line::arg_descriptor<std::string> arg_password_file = {"password-file", sw::tr("Wallet password file"), "", true};
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""}; const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false}; const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Create non-deterministic view and spend keys"), false}; const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Create non-deterministic view and spend keys"), false};
const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", sw::tr("Use daemon instance at port <arg> instead of 18081"), 0};
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", "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", sw::tr("Specify log file"), ""};
const command_line::arg_descriptor<bool> arg_testnet = {"testnet", sw::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", sw::tr("Restricts RPC to view-only commands"), false};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> 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<bool> 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<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0};
@ -272,7 +249,7 @@ namespace
return true; 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; return false;
} }
@ -661,7 +638,6 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st
simple_wallet::simple_wallet() simple_wallet::simple_wallet()
: m_allow_mismatched_daemon_version(false) : m_allow_mismatched_daemon_version(false)
, m_daemon_port(0)
, m_refresh_progress_reporter(*this) , m_refresh_progress_reporter(*this)
, m_idle_run(true) , m_idle_run(true)
, m_auto_refresh_enabled(false) , m_auto_refresh_enabled(false)
@ -966,303 +942,12 @@ void simple_wallet::print_seed(std::string seed)
// don't log // don't log
std::cout << seed << std::endl; std::cout << seed << std::endl;
} }
//----------------------------------------------------------------------------------------------------
bool simple_wallet::get_password(const boost::program_options::variables_map& vm, bool allow_entry, tools::password_container &pwd_container)
{
// has_arg returns true even when the parameter is not passed ??
const std::string gfj = command_line::get_arg(vm, arg_generate_from_json);
if (!gfj.empty()) {
// will be in the json file, if any
return true;
}
if (has_arg(vm, arg_password) && has_arg(vm, arg_password_file))
{
fail_msg_writer() << tr("can't specify more than one of --password and --password-file");
return false;
}
if (command_line::has_arg(vm, arg_password))
{
pwd_container.password(command_line::get_arg(vm, arg_password));
return true;
}
if (command_line::has_arg(vm, arg_password_file))
{
std::string password;
bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_password_file),
password);
if (!r)
{
fail_msg_writer() << tr("the password file specified could not be read");
return false;
}
// 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());
pwd_container.password(password.c_str());
return true;
}
if (allow_entry)
{
//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
bool r = pwd_container.read_password();
if (!r)
{
fail_msg_writer() << tr("failed to read wallet password");
return false;
}
return true;
}
fail_msg_writer() << tr("Wallet password not set.");
return false;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password)
{
bool testnet = command_line::get_arg(vm, arg_testnet);
std::string buf;
bool r = epee::file_io_utils::load_file_to_string(m_generate_from_json, buf);
if (!r) {
fail_msg_writer() << tr("Failed to load file ") << m_generate_from_json;
return false;
}
rapidjson::Document json;
if (json.Parse(buf.c_str()).HasParseError()) {
fail_msg_writer() << 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) {
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<const crypto::secret_key*>(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<const crypto::secret_key*>(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) bool simple_wallet::init(const boost::program_options::variables_map& vm)
{ {
if (!handle_command_line(vm)) if (!handle_command_line(vm))
return false; 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) 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\""); 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; 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_generate_new.empty() || m_restoring)
{ {
if (m_wallet_file.empty()) m_wallet_file = m_generate_new; // alias for simplicity later
std::string old_language; std::string old_language;
// check for recover flag. if present, require electrum word list (only recovery option for now). // check for recover flag. if present, require electrum word list (only recovery option for now).
if (m_restore_deterministic_wallet) 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()) if (!m_generate_from_view_key.empty())
{ {
m_wallet_file = m_generate_from_view_key;
// parse address // parse address
std::string address_string = command_line::input_line("Standard address: "); std::string address_string = command_line::input_line("Standard address: ");
if (std::cin.eof()) if (std::cin.eof())
@ -1361,7 +1018,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
cryptonote::account_public_address address; cryptonote::account_public_address address;
bool has_payment_id; bool has_payment_id;
crypto::hash8 new_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"); fail_msg_writer() << tr("failed to parse address");
return false; return false;
@ -1396,11 +1053,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false; 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")); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
} }
else if (!m_generate_from_keys.empty()) else if (!m_generate_from_keys.empty())
{ {
m_wallet_file = m_generate_from_keys;
// parse address // parse address
std::string address_string = command_line::input_line("Standard address: "); std::string address_string = command_line::input_line("Standard address: ");
if (std::cin.eof()) if (std::cin.eof())
@ -1412,7 +1070,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
cryptonote::account_public_address address; cryptonote::account_public_address address;
bool has_payment_id; bool has_payment_id;
crypto::hash8 new_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"); fail_msg_writer() << tr("failed to parse address");
return false; 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"); fail_msg_writer() << tr("view key does not match standard address");
return false; return false;
} }
bool r = new_wallet(vm, address, spendkey, viewkey);
bool r = new_wallet(m_wallet_file, pwd_container.password(), address, spendkey, viewkey, testnet);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
} }
else if (!m_generate_from_json.empty()) else if (!m_generate_from_json.empty())
{ {
std::string wallet_file, password; // we don't need to remember them m_wallet_file = m_generate_from_json;
if (!generate_from_json(vm, wallet_file, password)) if (!tools::wallet2::make_from_json(vm, m_wallet_file))
return false; return false;
} }
else else
{ {
bool r = new_wallet(m_wallet_file, pwd_container.password(), m_recovery_key, m_restore_deterministic_wallet, m_wallet_file = m_generate_new;
m_non_deterministic, testnet, old_language); 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")); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
} }
} }
else 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")); CHECK_AND_ASSERT_MES(r, false, tr("failed to open account"));
} }
assert(m_wallet);
m_wallet->callback(this);
return true; 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_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_keys = command_line::get_arg(vm, arg_generate_from_keys);
m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json); 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_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet); m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic); 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 (!m_wallet->check_connection(&same_version))
{ {
if (!silent) 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. " 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."); "Please make sure daemon is running or restart the wallet with the correct daemon address.");
return false; return false;
@ -1542,7 +1198,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent)
if (!m_allow_mismatched_daemon_version && !same_version) if (!m_allow_mismatched_daemon_version && !same_version)
{ {
if (!silent) 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."); tr("Either update one of them, or use --allow-mismatched-daemon-version.");
return false; 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 simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
bool recover, bool two_random, bool testnet, const std::string &old_language) 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) || 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)); 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; 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); m_wallet->set_seed_language(mnemonic_language);
// for a totally new account, we don't care about older blocks. // 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; crypto::secret_key recovery_val;
try 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: ") message_writer(epee::log_space::console_color_white, true) << tr("Generated new wallet: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->testnet()); << 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; 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; return false;
} }
m_wallet->init(m_daemon_address);
// convert rng value to electrum-style word list // convert rng value to electrum-style word list
std::string electrum_words; std::string electrum_words;
@ -1673,47 +1329,29 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const crypto::secret_key& viewkey, bool testnet) const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
{ {
m_wallet_file=wallet_file; auto rc = tools::wallet2::make_new(vm);
m_wallet = std::move(rc.first);
m_wallet.reset(new tools::wallet2(testnet)); if (!m_wallet)
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, 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; 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) if (m_restore_height)
m_wallet->set_refresh_from_block_height(m_restore_height); m_wallet->set_refresh_from_block_height(m_restore_height);
try 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: ") message_writer(epee::log_space::console_color_white, true) << tr("Generated new wallet: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->testnet()); << 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; return false;
} }
m_wallet->init(m_daemon_address);
return true; 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: ") << m_wallet_file;
return false;
}
std::string password;
try
{
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)
{ {
fail_msg_writer() << tr("wallet file path not valid: ") << wallet_file;
return false; return false;
} }
m_wallet_file=wallet_file;
m_wallet.reset(new tools::wallet2(testnet));
m_wallet->callback(this);
try
{
m_wallet->load(m_wallet_file, password);
message_writer(epee::log_space::console_color_white, true) << message_writer(epee::log_space::console_color_white, true) <<
(m_wallet->watch_only() ? tr("Opened watch-only wallet") : tr("Opened wallet")) << ": " (m_wallet->watch_only() ? tr("Opened watch-only wallet") : tr("Opened wallet")) << ": "
<< m_wallet->get_account().get_public_address_str(m_wallet->testnet()); << m_wallet->get_account().get_public_address_str(m_wallet->testnet());
@ -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(); fail_msg_writer() << tr("failed to load wallet: ") << e.what();
// only suggest removing cache if the password was actually correct // only suggest removing cache if the password was actually correct
if (m_wallet->verify_password(password)) 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")) % wallet_file; fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file;
return false; return false;
} }
m_wallet->init(m_daemon_address);
success_msg_writer() << success_msg_writer() <<
"**********************************************************************\n" << "**********************************************************************\n" <<
tr("Use \"help\" command to see the list of available commands.\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<std::string>& args)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
assert(m_wallet);
COMMAND_RPC_START_MINING::request req; COMMAND_RPC_START_MINING::request req;
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); 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<std::string>& args)
} }
COMMAND_RPC_START_MINING::response res; 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); std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) if (err.empty())
success_msg_writer() << tr("Mining started in daemon"); success_msg_writer() << tr("Mining started in daemon");
@ -1924,9 +1562,10 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
assert(m_wallet);
COMMAND_RPC_STOP_MINING::request req; COMMAND_RPC_STOP_MINING::request req;
COMMAND_RPC_STOP_MINING::response res; 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); std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) if (err.empty())
success_msg_writer() << tr("Mining stopped in daemon"); success_msg_writer() << tr("Mining stopped in daemon");
@ -1940,9 +1579,10 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
assert(m_wallet);
COMMAND_RPC_SAVE_BC::request req; COMMAND_RPC_SAVE_BC::request req;
COMMAND_RPC_SAVE_BC::response res; 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); std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) if (err.empty())
success_msg_writer() << tr("Blockchain saved"); success_msg_writer() << tr("Blockchain saved");
@ -2207,9 +1847,14 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) 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::request req;
COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>(); COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>();
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); err = interpret_rpc_response(r, res.status);
return res.height; return res.height;
} }
@ -3418,6 +3063,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
assert(m_wallet);
cryptonote::blobdata txid_data; cryptonote::blobdata txid_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], 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<std::string> &args_)
COMMAND_RPC_GET_TRANSACTIONS::request req; COMMAND_RPC_GET_TRANSACTIONS::request req;
COMMAND_RPC_GET_TRANSACTIONS::response res; COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); 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)) (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
{ {
fail_msg_writer() << tr("failed to get transaction from daemon"); fail_msg_writer() << tr("failed to get transaction from daemon");
@ -4261,231 +3907,42 @@ void simple_wallet::interrupt()
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
#ifdef WIN32 po::options_description desc_params(wallet_args::tr("Wallet options"));
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); tools::wallet2::init_options(desc_params);
#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"));
command_line::add_arg(desc_params, arg_wallet_file); 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_new_wallet);
command_line::add_arg(desc_params, arg_generate_from_view_key); 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_keys);
command_line::add_arg(desc_params, arg_generate_from_json); 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_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_restore_deterministic_wallet );
command_line::add_arg(desc_params, arg_non_deterministic ); 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_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_trusted_daemon);
command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version); command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version);
command_line::add_arg(desc_params, arg_restore_height); command_line::add_arg(desc_params, arg_restore_height);
tools::wallet_rpc_server::init_options(desc_params);
po::positional_options_description positional_options; po::positional_options_description positional_options;
positional_options.add(arg_command.name, -1); positional_options.add(arg_command.name, -1);
i18n_set_language("translations", "monero", lang); const auto vm = wallet_args::main(
argc, argv,
po::options_description desc_all; "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]",
desc_all.add(desc_general).add(desc_params); desc_params,
cryptonote::simple_wallet w; positional_options
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))
{
success_msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
success_msg_writer() << sw::tr("Usage:") << " monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]";
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)) if (!vm)
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); cryptonote::simple_wallet w;
bool restricted = command_line::get_arg(vm, arg_restricted); const bool r = w.init(*vm);
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();
}
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
{
//runs wallet with console interface
r = w.init(vm);
CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet")); CHECK_AND_ASSERT_MES(r, 1, sw::tr("Failed to initialize wallet"));
std::vector<std::string> command = command_line::get_arg(vm, arg_command); std::vector<std::string> command = command_line::get_arg(*vm, arg_command);
if (!command.empty()) if (!command.empty())
{ {
w.process_command(command); w.process_command(command);
@ -4513,7 +3970,6 @@ int main(int argc, char* argv[])
w.deinit(); w.deinit();
} }
}
return 0; return 0;
//CATCH_ENTRY_L0("main", 1); //CATCH_ENTRY_L0("main", 1);
} }

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