Created monero-wallet-rpc, moving functionality from monero-wallet-cli
This commit is contained in:
parent
38727f7d2f
commit
358e068e87
|
@ -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})
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)};
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue