monero/src/wallet/wallet_rpc_server.cpp

1913 lines
71 KiB
C++
Raw Normal View History

// Copyright (c) 2014-2017, The Monero Project
2014-07-23 15:03:52 +02:00
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
#include <cstdint>
2014-04-02 18:00:17 +02:00
#include "include_base_utils.h"
using namespace epee;
#include "wallet_rpc_server.h"
#include "wallet/wallet_args.h"
2014-04-02 18:00:17 +02:00
#include "common/command_line.h"
#include "common/i18n.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "wallet_rpc_server_commands_defs.h"
2014-04-02 18:00:17 +02:00
#include "misc_language.h"
#include "string_coding.h"
2014-05-03 18:19:43 +02:00
#include "string_tools.h"
2014-04-02 18:00:17 +02:00
#include "crypto/hash.h"
#include "mnemonics/electrum-words.h"
#include "rpc/rpc_args.h"
2017-03-19 12:31:38 +01:00
#include "rpc/core_rpc_server_commands_defs.h"
2014-04-02 18:00:17 +02:00
Change logging to easylogging++ This replaces the epee and data_loggers logging systems with a single one, and also adds filename:line and explicit severity levels. Categories may be defined, and logging severity set by category (or set of categories). epee style 0-4 log level maps to a sensible severity configuration. Log files now also rotate when reaching 100 MB. To select which logs to output, use the MONERO_LOGS environment variable, with a comma separated list of categories (globs are supported), with their requested severity level after a colon. If a log matches more than one such setting, the last one in the configuration string applies. A few examples: This one is (mostly) silent, only outputting fatal errors: MONERO_LOGS=*:FATAL This one is very verbose: MONERO_LOGS=*:TRACE This one is totally silent (logwise): MONERO_LOGS="" This one outputs all errors and warnings, except for the "verify" category, which prints just fatal errors (the verify category is used for logs about incoming transactions and blocks, and it is expected that some/many will fail to verify, hence we don't want the spam): MONERO_LOGS=*:WARNING,verify:FATAL Log levels are, in decreasing order of priority: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE Subcategories may be added using prefixes and globs. This example will output net.p2p logs at the TRACE level, but all other net* logs only at INFO: MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE Logs which are intended for the user (which Monero was using a lot through epee, but really isn't a nice way to go things) should use the "global" category. There are a few helper macros for using this category, eg: MGINFO("this shows up by default") or MGINFO_RED("this is red"), to try to keep a similar look and feel for now. Existing epee log macros still exist, and map to the new log levels, but since they're used as a "user facing" UI element as much as a logging system, they often don't map well to log severities (ie, a log level 0 log may be an error, or may be something we want the user to see, such as an important info). In those cases, I tried to use the new macros. In other cases, I left the existing macros in. When modifying logs, it is probably best to switch to the new macros with explicit levels. The --log-level options and set_log commands now also accept category settings, in addition to the epee style log levels.
2017-01-01 17:34:23 +01:00
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
namespace
2014-04-02 18:00:17 +02:00
{
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<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false};
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
constexpr const char default_rpc_username[] = "monero";
}
2014-04-02 18:00:17 +02:00
namespace tools
{
const char* wallet_rpc_server::tr(const char* str)
2014-04-02 18:00:17 +02:00
{
return i18n_translate(str, "tools::wallet_rpc_server");
2014-04-02 18:00:17 +02:00
}
2014-04-02 18:00:17 +02:00
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false)
{
}
2014-04-02 18:00:17 +02:00
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
{
m_wallet = cr;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-04-02 18:00:17 +02:00
bool wallet_rpc_server::run()
{
m_stop = false;
2014-04-02 18:00:17 +02:00
m_net_server.add_idle_handler([this](){
try {
if (m_wallet) m_wallet->refresh();
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
2014-04-02 18:00:17 +02:00
return true;
}, 20000);
m_net_server.add_idle_handler([this](){
if (m_stop.load(std::memory_order_relaxed))
{
send_stop_signal();
return false;
}
return true;
}, 500);
2014-04-02 18:00:17 +02:00
//DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::run(1, true);
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::stop()
2014-04-02 18:00:17 +02:00
{
if (m_wallet)
{
m_wallet->store();
delete m_wallet;
m_wallet = NULL;
}
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::init(const boost::program_options::variables_map *vm)
{
auto rpc_config = cryptonote::rpc_args::process(*vm);
if (!rpc_config)
return false;
m_vm = vm;
tools::wallet2 *walvars;
std::unique_ptr<tools::wallet2> tmpwal;
if (m_wallet)
walvars = m_wallet;
else
{
tmpwal = tools::wallet2::make_dummy(*m_vm);
walvars = tmpwal.get();
}
boost::optional<epee::net_utils::http::login> http_login{};
std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port);
const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login);
m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon);
if (!command_line::has_arg(*m_vm, arg_trusted_daemon))
{
if (tools::is_local_address(walvars->get_daemon_address()))
{
MINFO(tr("Daemon is local, assuming trusted"));
m_trusted_daemon = true;
}
}
if (command_line::has_arg(*m_vm, arg_wallet_dir))
{
m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir);
#ifdef _WIN32
#define MKDIR(path, mode) mkdir(path)
#else
#define MKDIR(path, mode) mkdir(path, mode)
#endif
MKDIR(m_wallet_dir.c_str(), 0700);
}
if (disable_auth)
{
if (rpc_config->login)
{
const cryptonote::rpc_args::descriptors arg{};
LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg.rpc_login.name);
return false;
}
}
else // auth enabled
{
if (!rpc_config->login)
{
std::array<std::uint8_t, 16> rand_128bit{{}};
crypto::rand(rand_128bit.size(), rand_128bit.data());
http_login.emplace(
default_rpc_username,
string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size())
);
std::string temp = "monero-wallet-rpc." + bind_port + ".login";
rpc_login_file = tools::private_file::create(temp);
if (!rpc_login_file.handle())
{
LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
return false;
}
std::fputs(http_login->username.c_str(), rpc_login_file.handle());
std::fputc(':', rpc_login_file.handle());
std::fputs(http_login->password.c_str(), rpc_login_file.handle());
std::fflush(rpc_login_file.handle());
if (std::ferror(rpc_login_file.handle()))
{
LOG_ERROR(tr("Error writing to file ") << temp);
return false;
}
LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
}
else // chosen user/pass
{
http_login.emplace(
std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()
);
}
assert(bool(http_login));
} // end auth enabled
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
2017-03-19 12:31:38 +01:00
2014-04-02 18:00:17 +02:00
m_net_server.set_threads_prefix("RPC");
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login)
);
2014-04-02 18:00:17 +02:00
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::not_open(epee::json_rpc::error& er)
{
er.code = WALLET_RPC_ERROR_CODE_NOT_OPEN;
er.message = "No wallet file";
return false;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet_rpc_server::adjust_mixin(uint64_t mixin)
{
if (mixin < 4 && m_wallet->use_fork_rules(6, 10)) {
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
mixin = 4;
}
else if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) {
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
mixin = 2;
}
return mixin;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
{
entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
entry.payment_id = string_tools::pod_to_hex(payment_id);
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
entry.payment_id = entry.payment_id.substr(0,16);
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.unlock_time = pd.m_unlock_time;
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "in";
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
{
entry.txid = string_tools::pod_to_hex(txid);
entry.payment_id = string_tools::pod_to_hex(pd.m_payment_id);
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
entry.payment_id = entry.payment_id.substr(0,16);
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
entry.unlock_time = pd.m_unlock_time;
entry.fee = pd.m_amount_in - pd.m_amount_out;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
entry.amount = pd.m_amount_in - change - entry.fee;
entry.note = m_wallet->get_tx_note(txid);
for (const auto &d: pd.m_dests) {
entry.destinations.push_back(wallet_rpc::transfer_destination());
wallet_rpc::transfer_destination &td = entry.destinations.back();
td.amount = d.amount;
td.address = get_account_address_as_str(m_wallet->testnet(), d.addr);
}
entry.type = "out";
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
{
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
entry.txid = string_tools::pod_to_hex(txid);
entry.payment_id = string_tools::pod_to_hex(pd.m_payment_id);
entry.payment_id = string_tools::pod_to_hex(pd.m_payment_id);
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
entry.payment_id = entry.payment_id.substr(0,16);
entry.height = 0;
entry.timestamp = pd.m_timestamp;
entry.fee = pd.m_amount_in - pd.m_amount_out;
entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
entry.unlock_time = pd.m_tx.unlock_time;
entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
{
entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
entry.payment_id = string_tools::pod_to_hex(payment_id);
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
entry.payment_id = entry.payment_id.substr(0,16);
entry.height = 0;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.unlock_time = pd.m_unlock_time;
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "pool";
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
2014-04-02 18:00:17 +02:00
{
if (!m_wallet) return not_open(er);
2014-04-02 18:00:17 +02:00
try
{
res.balance = m_wallet->balance();
res.unlocked_balance = m_wallet->unlocked_balance();
2014-04-02 18:00:17 +02:00
}
2017-03-18 13:56:07 +01:00
catch (const std::exception& e)
2014-04-02 18:00:17 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er)
2014-05-25 00:20:46 +02:00
{
if (!m_wallet) return not_open(er);
2014-05-25 00:20:46 +02:00
try
{
res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
2014-05-25 00:20:46 +02:00
}
2017-03-18 13:56:07 +01:00
catch (const std::exception& e)
2014-05-25 00:20:46 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
2015-12-23 17:04:04 +01:00
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
2015-12-23 17:04:04 +01:00
try
{
res.height = m_wallet->get_blockchain_current_height();
2015-12-23 17:04:04 +01:00
}
2017-03-18 13:56:07 +01:00
catch (const std::exception& e)
2015-12-23 17:04:04 +01:00
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
2014-05-25 00:20:46 +02:00
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
2014-04-02 18:00:17 +02:00
{
crypto::hash8 integrated_payment_id = cryptonote::null_hash8;
std::string extra_nonce;
for (auto it = destinations.begin(); it != destinations.end(); it++)
2014-04-02 18:00:17 +02:00
{
cryptonote::tx_destination_entry de;
bool has_payment_id;
crypto::hash8 new_payment_id;
er.message = "";
if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
er.message = std::string("Invalid DNSSEC for ") + url;
return {};
}
if (addresses.empty())
{
er.message = std::string("No Monero address found at ") + url;
return {};
}
return addresses[0];
}))
2014-04-02 18:00:17 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
if (er.message.empty())
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
2014-04-02 18:00:17 +02:00
return false;
}
de.amount = it->amount;
dsts.push_back(de);
if (has_payment_id)
{
if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash8)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "A single payment id is allowed per transaction";
return false;
}
integrated_payment_id = new_payment_id;
cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, integrated_payment_id);
/* Append Payment ID data into extra */
if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Something went wrong with integrated payment_id.";
return false;
}
}
2014-04-02 18:00:17 +02:00
}
if (!payment_id.empty())
{
/* Just to clarify */
const std::string& payment_id_str = payment_id;
crypto::hash long_payment_id;
crypto::hash8 short_payment_id;
/* Parse payment ID */
if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) {
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id);
}
/* or short payment ID */
else if (wallet2::parse_short_payment_id(payment_id_str, short_payment_id)) {
cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, short_payment_id);
}
else {
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 16 or 64 character string";
return false;
}
/* Append Payment ID data into extra */
if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string";
return false;
}
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er)
{
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
LOG_PRINT_L3("on_transfer starts");
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
// validate the transfer requested and populate dsts & extra
if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
{
return false;
}
try
{
uint64_t mixin = adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = "Transaction would be too large. try /transfer_split.";
return false;
}
if (!req.do_not_relay)
m_wallet->commit_tx(ptx_vector);
// populate response with tx hash
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
if (req.get_tx_key)
2016-07-12 00:14:58 +02:00
{
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
2016-07-12 00:14:58 +02:00
}
2016-11-16 19:56:45 +01:00
res.fee = ptx_vector.back().fee;
if (req.get_tx_hex)
{
cryptonote::blobdata blob;
tx_to_blob(ptx_vector.back().tx, blob);
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
}
return true;
}
catch (const tools::error::daemon_busy& e)
{
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
er.message = e.what();
return false;
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = e.what();
return false;
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er)
{
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
{
return false;
}
2014-04-02 18:00:17 +02:00
try
{
uint64_t mixin = adjust_mixin(req.mixin);
uint64_t ptx_amount;
std::vector<wallet2::pending_tx> ptx_vector;
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
if (!req.do_not_relay)
{
LOG_PRINT_L2("on_transfer_split calling commit_tx");
m_wallet->commit_tx(ptx_vector);
LOG_PRINT_L2("on_transfer_split called commit_tx");
}
// populate response with tx hashes
for (auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
2016-07-12 00:14:58 +02:00
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
2016-07-12 00:14:58 +02:00
}
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
ptx_amount = 0;
for(auto & dt: ptx.dests)
ptx_amount += dt.amount;
res.amount_list.push_back(ptx_amount);
2016-11-16 19:56:45 +01:00
res.fee_list.push_back(ptx.fee);
if (req.get_tx_hex)
{
cryptonote::blobdata blob;
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
}
return true;
}
catch (const tools::error::daemon_busy& e)
{
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
er.message = e.what();
return false;
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = e.what();
return false;
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
try
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
if (!req.do_not_relay)
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
2016-07-12 00:14:58 +02:00
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
2016-07-12 00:14:58 +02:00
}
2016-11-16 19:56:45 +01:00
res.fee_list.push_back(ptx.fee);
if (req.get_tx_hex)
{
cryptonote::blobdata blob;
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
}
2014-04-02 18:00:17 +02:00
return true;
}
catch (const tools::error::daemon_busy& e)
{
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
er.message = e.what();
2014-05-03 18:19:43 +02:00
return false;
2014-04-02 18:00:17 +02:00
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = e.what();
2014-05-03 18:19:43 +02:00
return false;
2014-04-02 18:00:17 +02:00
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er)
{
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
// validate the transfer requested and populate dsts & extra
std::list<wallet_rpc::transfer_destination> destination;
destination.push_back(wallet_rpc::transfer_destination());
destination.back().amount = 0;
destination.back().address = req.address;
if (!validate_transfer(destination, req.payment_id, dsts, extra, er))
{
return false;
}
try
{
uint64_t mixin = adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
if (!req.do_not_relay)
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
2016-07-12 00:14:58 +02:00
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
2016-07-12 00:14:58 +02:00
}
if (req.get_tx_hex)
{
cryptonote::blobdata blob;
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
}
return true;
}
catch (const tools::error::daemon_busy& e)
{
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
er.message = e.what();
return false;
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = e.what();
return false;
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
2014-04-02 18:00:17 +02:00
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
2014-05-03 18:19:43 +02:00
return false;
2014-04-02 18:00:17 +02:00
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
try
{
crypto::hash8 payment_id;
if (req.payment_id.empty())
{
payment_id = crypto::rand<crypto::hash8>();
}
else
{
if (!tools::wallet2::parse_short_payment_id(req.payment_id,payment_id))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Invalid payment ID";
return false;
}
}
res.integrated_address = m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet());
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
return true;
}
2017-03-18 13:56:07 +01:00
catch (const std::exception &e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
try
{
cryptonote::account_public_address address;
crypto::hash8 payment_id;
bool has_payment_id;
if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), req.integrated_address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Invalid address";
return false;
}
if(!has_payment_id)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Address is not an integrated address";
return false;
}
res.standard_address = get_account_address_as_str(m_wallet->testnet(),address);
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
return true;
}
2017-03-18 13:56:07 +01:00
catch (const std::exception &e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er)
2014-04-02 18:00:17 +02:00
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
2014-04-02 18:00:17 +02:00
try
{
m_wallet->store();
2014-04-02 18:00:17 +02:00
}
2017-03-18 13:56:07 +01:00
catch (const std::exception& e)
2014-04-02 18:00:17 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er)
2014-05-03 18:19:43 +02:00
{
if (!m_wallet) return not_open(er);
2014-05-03 18:19:43 +02:00
crypto::hash payment_id;
crypto::hash8 payment_id8;
2014-05-03 18:19:43 +02:00
cryptonote::blobdata payment_id_blob;
if(!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
2016-12-04 14:13:54 +01:00
er.message = "Payment ID has invalid format";
2014-05-03 18:19:43 +02:00
return false;
}
if(sizeof(payment_id) == payment_id_blob.size())
{
payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_blob.data());
}
else if(sizeof(payment_id8) == payment_id_blob.size())
{
payment_id8 = *reinterpret_cast<const crypto::hash8*>(payment_id_blob.data());
memcpy(payment_id.data, payment_id8.data, 8);
memset(payment_id.data + 8, 0, 24);
}
else
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment ID has invalid size: " + req.payment_id;
return false;
}
2014-05-03 18:19:43 +02:00
res.payments.clear();
std::list<wallet2::payment_details> payment_list;
m_wallet->get_payments(payment_id, payment_list);
2014-07-22 18:00:25 +02:00
for (auto & payment : payment_list)
2014-05-03 18:19:43 +02:00
{
wallet_rpc::payment_details rpc_payment;
2014-07-22 18:00:25 +02:00
rpc_payment.payment_id = req.payment_id;
2014-05-03 18:19:43 +02:00
rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash);
rpc_payment.amount = payment.m_amount;
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
res.payments.push_back(rpc_payment);
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er)
2014-07-22 18:00:25 +02:00
{
res.payments.clear();
if (!m_wallet) return not_open(er);
2014-07-22 18:00:25 +02:00
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
if (req.payment_ids.empty())
{
std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list;
m_wallet->get_payments(payment_list, req.min_block_height);
for (auto & payment : payment_list)
{
wallet_rpc::payment_details rpc_payment;
rpc_payment.payment_id = epee::string_tools::pod_to_hex(payment.first);
rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.second.m_tx_hash);
rpc_payment.amount = payment.second.m_amount;
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
res.payments.push_back(std::move(rpc_payment));
}
return true;
}
2014-07-22 18:00:25 +02:00
for (auto & payment_id_str : req.payment_ids)
{
crypto::hash payment_id;
crypto::hash8 payment_id8;
2014-07-22 18:00:25 +02:00
cryptonote::blobdata payment_id_blob;
// TODO - should the whole thing fail because of one bad id?
if(!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_blob))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment ID has invalid format: " + payment_id_str;
return false;
}
if(sizeof(payment_id) == payment_id_blob.size())
{
payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_blob.data());
}
else if(sizeof(payment_id8) == payment_id_blob.size())
{
payment_id8 = *reinterpret_cast<const crypto::hash8*>(payment_id_blob.data());
memcpy(payment_id.data, payment_id8.data, 8);
memset(payment_id.data + 8, 0, 24);
}
else
2014-07-22 18:00:25 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment ID has invalid size: " + payment_id_str;
return false;
}
std::list<wallet2::payment_details> payment_list;
m_wallet->get_payments(payment_id, payment_list, req.min_block_height);
2014-07-22 18:00:25 +02:00
for (auto & payment : payment_list)
{
wallet_rpc::payment_details rpc_payment;
rpc_payment.payment_id = payment_id_str;
rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash);
rpc_payment.amount = payment.m_amount;
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
res.payments.push_back(std::move(rpc_payment));
}
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0)
{
er.code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE;
er.message = "Transfer type must be one of: all, available, or unavailable";
return false;
}
bool filter = false;
bool available = false;
if (req.transfer_type.compare("available") == 0)
{
filter = true;
available = true;
}
else if (req.transfer_type.compare("unavailable") == 0)
{
filter = true;
available = false;
}
wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
bool transfers_found = false;
for (const auto& td : transfers)
{
if (!filter || available != td.m_spent)
{
if (!transfers_found)
{
transfers_found = true;
}
auto txBlob = t_serializable_object_to_blob(td.m_tx);
wallet_rpc::transfer_details rpc_transfers;
rpc_transfers.amount = td.amount();
rpc_transfers.spent = td.m_spent;
rpc_transfers.global_index = td.m_global_output_index;
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.tx_size = txBlob.size();
res.transfers.push_back(rpc_transfers);
}
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
if (req.key_type.compare("mnemonic") == 0)
{
if (!m_wallet->get_seed(res.key))
{
er.message = "The wallet is non-deterministic. Cannot display seed.";
return false;
}
}
else if(req.key_type.compare("view_key") == 0)
{
res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
}
else
{
er.message = "key_type " + req.key_type + " not found";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
try
{
m_wallet->rescan_blockchain();
}
2017-03-18 13:56:07 +01:00
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
res.signature = m_wallet->sign(req.data);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id;
er.message = "";
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
er.message = std::string("Invalid DNSSEC for ") + url;
return {};
}
if (addresses.empty())
{
er.message = std::string("No Monero address found at ") + url;
return {};
}
return addresses[0];
}))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
return false;
}
res.good = m_wallet->verify(req.data, address, req.signature);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
2015-12-05 15:53:37 +01:00
bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
2015-12-05 15:53:37 +01:00
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
try
{
m_wallet->store();
m_stop.store(true, std::memory_order_relaxed);
2015-12-05 15:53:37 +01:00
}
2017-03-18 13:56:07 +01:00
catch (const std::exception& e)
2015-12-05 15:53:37 +01:00
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
2016-04-20 19:19:42 +02:00
bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
2016-04-20 19:19:42 +02:00
if (req.txids.size() != req.notes.size())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Different amount of txids and notes";
return false;
}
std::list<crypto::hash> txids;
std::list<std::string>::const_iterator i = req.txids.begin();
while (i != req.txids.end())
{
cryptonote::blobdata txid_blob;
2017-02-14 20:35:44 +01:00
if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
2016-04-20 19:19:42 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "TX ID has invalid format";
return false;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
txids.push_back(txid);
}
std::list<crypto::hash>::const_iterator il = txids.begin();
std::list<std::string>::const_iterator in = req.notes.begin();
while (il != txids.end())
{
m_wallet->set_tx_note(*il++, *in++);
2016-04-20 19:19:42 +02:00
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er)
{
res.notes.clear();
if (!m_wallet) return not_open(er);
2016-04-20 19:19:42 +02:00
std::list<crypto::hash> txids;
std::list<std::string>::const_iterator i = req.txids.begin();
while (i != req.txids.end())
{
cryptonote::blobdata txid_blob;
2017-02-14 20:35:44 +01:00
if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
2016-04-20 19:19:42 +02:00
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "TX ID has invalid format";
return false;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
txids.push_back(txid);
}
std::list<crypto::hash>::const_iterator il = txids.begin();
while (il != txids.end())
{
res.notes.push_back(m_wallet->get_tx_note(*il++));
2016-04-20 19:19:42 +02:00
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
uint64_t min_height = 0, max_height = (uint64_t)-1;
if (req.filter_by_height)
{
min_height = req.min_height;
max_height = req.max_height;
}
if (req.in)
{
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_payments(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.in.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second);
}
}
if (req.out)
{
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
m_wallet->get_payments_out(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.out.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.out.back(), i->first, i->second);
}
}
if (req.pending || req.failed) {
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
m_wallet->get_unconfirmed_payments_out(upayments);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
if (!((req.failed && is_failed) || (!is_failed && req.pending)))
continue;
std::list<wallet_rpc::transfer_entry> &entries = is_failed ? res.failed : res.pending;
entries.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(entries.back(), i->first, i->second);
}
}
if (req.pool)
{
m_wallet->update_pool_state();
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.pool.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.pool.back(), i->first, i->second);
}
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
crypto::hash txid;
cryptonote::blobdata txid_blob;
if(!epee::string_tools::parse_hexstr_to_binbuff(req.txid, txid_blob))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction ID has invalid format";
return false;
}
if(sizeof(txid) == txid_blob.size())
{
txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
}
else
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction ID has invalid size: " + req.txid;
return false;
}
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_payments(payments, 0);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
return true;
}
}
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
m_wallet->get_payments_out(payments_out, 0);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
}
}
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
m_wallet->get_unconfirmed_payments_out(upayments);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
}
}
m_wallet->update_pool_state();
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
m_wallet->get_unconfirmed_payments(pool_payments);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
}
}
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction not found.";
return false;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
try
{
std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet->export_key_images();
res.signed_key_images.resize(ski.size());
for (size_t n = 0; n < ski.size(); ++n)
{
res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski[n].first);
res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski[n].second);
}
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
if (!m_trusted_daemon)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "This command requires a trusted daemon.";
return false;
}
try
{
std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
ski.resize(req.signed_key_images.size());
for (size_t n = 0; n < ski.size(); ++n)
{
cryptonote::blobdata bd;
2017-02-14 20:35:44 +01:00
if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd) || bd.size() != sizeof(crypto::key_image))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
er.message = "failed to parse key image";
return false;
}
ski[n].first = *reinterpret_cast<const crypto::key_image*>(bd.data());
2017-02-14 20:35:44 +01:00
if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd) || bd.size() != sizeof(crypto::signature))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE;
er.message = "failed to parse signature";
return false;
}
ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data());
}
uint64_t spent = 0, unspent = 0;
uint64_t height = m_wallet->import_key_images(ski, spent, unspent);
res.spent = spent;
res.unspent = unspent;
res.height = height;
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er)
{
std::string error;
std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error);
if (uri.empty())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_URI;
er.message = std::string("Cannot make URI from supplied parameters: ") + error;
return false;
}
res.uri = uri;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
std::string error;
if (!m_wallet->parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_URI;
er.message = "Error parsing URI: " + error;
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
const auto ab = m_wallet->get_address_book();
if (req.entries.empty())
{
uint64_t idx = 0;
for (const auto &entry: ab)
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
}
else
{
for (uint64_t idx: req.entries)
{
if (idx >= ab.size())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX;
er.message = "Index out of range: " + std::to_string(idx);
return false;
}
const auto &entry = ab[idx];
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
}
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id8;
crypto::hash payment_id = cryptonote::null_hash;
er.message = "";
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
er.message = std::string("Invalid DNSSEC for ") + url;
return {};
}
if (addresses.empty())
{
er.message = std::string("No Monero address found at ") + url;
return {};
}
return addresses[0];
}))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
if (er.message.empty())
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
return false;
}
if (has_payment_id)
{
memcpy(payment_id.data, payment_id8.data, 8);
memset(payment_id.data + 8, 0, 24);
}
if (!req.payment_id.empty())
{
if (has_payment_id)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Separate payment ID given with integrated address";
return false;
}
crypto::hash long_payment_id;
crypto::hash8 short_payment_id;
if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
{
if (!wallet2::parse_short_payment_id(req.payment_id, payment_id8))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 16 or 64 character string";
return false;
}
else
{
memcpy(payment_id.data, payment_id8.data, 8);
memset(payment_id.data + 8, 0, 24);
}
}
}
if (!m_wallet->add_address_book_row(address, payment_id, req.description))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to add address book entry";
return false;
}
res.index = m_wallet->get_address_book().size();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
const auto ab = m_wallet->get_address_book();
if (req.index >= ab.size())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX;
er.message = "Index out of range: " + std::to_string(req.index);
return false;
}
if (!m_wallet->delete_address_book_row(req.index))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to delete address book entry";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
try
{
m_wallet->rescan_spent();
return true;
}
catch (const std::exception &e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
2017-03-19 12:31:38 +01:00
bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
2017-03-19 12:31:38 +01:00
if (!m_trusted_daemon)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "This command requires a trusted daemon.";
return false;
}
size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "The specified number of threads is inappropriate.";
return false;
}
cryptonote::COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req);
daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
2017-03-19 12:31:38 +01:00
daemon_req.threads_count = req.threads_count;
daemon_req.do_background_mining = req.do_background_mining;
daemon_req.ignore_battery = req.ignore_battery;
cryptonote::COMMAND_RPC_START_MINING::response daemon_res;
bool r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_res, m_http_client);
if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Couldn't start mining due to unknown error.";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er)
{
cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req;
cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res;
bool r = net_utils::invoke_http_json("/stop_mining", daemon_req, daemon_res, m_http_client);
if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Couldn't stop mining due to unknown error.";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er)
{
crypto::ElectrumWords::get_language_list(res.languages);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er)
{
if (m_wallet_dir.empty())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "No wallet dir configured";
return false;
}
namespace po = boost::program_options;
po::variables_map vm2;
const char *ptr = strchr(req.filename.c_str(), '/');
#ifdef _WIN32
if (!ptr)
ptr = strchr(req.filename.c_str(), '\\');
if (!ptr)
ptr = strchr(req.filename.c_str(), ':');
#endif
if (ptr)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Invalid filename";
return false;
}
std::string wallet_file = m_wallet_dir + "/" + req.filename;
{
std::vector<std::string> languages;
crypto::ElectrumWords::get_language_list(languages);
std::vector<std::string>::iterator it;
std::string wallet_file;
char *ptr;
it = std::find(languages.begin(), languages.end(), req.language);
if (it == languages.end())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Unknown language";
return false;
}
}
{
po::options_description desc("dummy");
const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
const char *argv[4];
int argc = 3;
argv[0] = "wallet-rpc";
argv[1] = "--password";
argv[2] = req.password.c_str();
argv[3] = NULL;
vm2 = *m_vm;
command_line::add_arg(desc, arg_password);
po::store(po::parse_command_line(argc, argv, desc), vm2);
}
std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first;
if (!wal)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to create wallet";
return false;
}
wal->set_seed_language(req.language);
cryptonote::COMMAND_RPC_GET_HEIGHT::request hreq;
cryptonote::COMMAND_RPC_GET_HEIGHT::response hres;
hres.height = 0;
bool r = net_utils::invoke_http_json("/getheight", hreq, hres, m_http_client);
wal->set_refresh_from_block_height(hres.height);
crypto::secret_key dummy_key;
try {
wal->generate(wallet_file, req.password, dummy_key, false, false);
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to generate wallet";
return false;
}
if (m_wallet)
delete m_wallet;
m_wallet = wal.release();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er)
{
if (m_wallet_dir.empty())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "No wallet dir configured";
return false;
}
namespace po = boost::program_options;
po::variables_map vm2;
const char *ptr = strchr(req.filename.c_str(), '/');
#ifdef _WIN32
if (!ptr)
ptr = strchr(req.filename.c_str(), '\\');
if (!ptr)
ptr = strchr(req.filename.c_str(), ':');
#endif
if (ptr)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Invalid filename";
return false;
}
std::string wallet_file = m_wallet_dir + "/" + req.filename;
{
po::options_description desc("dummy");
const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
const char *argv[4];
int argc = 3;
argv[0] = "wallet-rpc";
argv[1] = "--password";
argv[2] = req.password.c_str();
argv[3] = NULL;
vm2 = *m_vm;
command_line::add_arg(desc, arg_password);
po::store(po::parse_command_line(argc, argv, desc), vm2);
}
std::unique_ptr<tools::wallet2> wal;
try {
wal = tools::wallet2::make_from_file(vm2, wallet_file).first;
}
catch (const std::exception& e)
{
wal = nullptr;
}
if (!wal)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to open wallet";
return false;
}
if (m_wallet)
delete m_wallet;
m_wallet = wal.release();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
2014-05-03 18:19:43 +02:00
}
2015-12-23 17:04:04 +01:00
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_port);
command_line::add_arg(desc_params, arg_disable_rpc_login);
command_line::add_arg(desc_params, arg_trusted_daemon);
cryptonote::rpc_args::init_options(desc_params);
command_line::add_arg(desc_params, arg_wallet_file);
command_line::add_arg(desc_params, arg_from_json);
command_line::add_arg(desc_params, arg_wallet_dir);
const auto vm = wallet_args::main(
argc, argv,
"monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
desc_params,
po::positional_options_description(),
2017-02-26 23:59:46 +01:00
"monero-wallet-rpc.log",
true
);
if (!vm)
{
return 1;
}
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);
const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
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_dir.empty())
{
wal = NULL;
goto just_dir;
}
if (wallet_file.empty() && from_json.empty())
{
LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
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)
{
Change logging to easylogging++ This replaces the epee and data_loggers logging systems with a single one, and also adds filename:line and explicit severity levels. Categories may be defined, and logging severity set by category (or set of categories). epee style 0-4 log level maps to a sensible severity configuration. Log files now also rotate when reaching 100 MB. To select which logs to output, use the MONERO_LOGS environment variable, with a comma separated list of categories (globs are supported), with their requested severity level after a colon. If a log matches more than one such setting, the last one in the configuration string applies. A few examples: This one is (mostly) silent, only outputting fatal errors: MONERO_LOGS=*:FATAL This one is very verbose: MONERO_LOGS=*:TRACE This one is totally silent (logwise): MONERO_LOGS="" This one outputs all errors and warnings, except for the "verify" category, which prints just fatal errors (the verify category is used for logs about incoming transactions and blocks, and it is expected that some/many will fail to verify, hence we don't want the spam): MONERO_LOGS=*:WARNING,verify:FATAL Log levels are, in decreasing order of priority: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE Subcategories may be added using prefixes and globs. This example will output net.p2p logs at the TRACE level, but all other net* logs only at INFO: MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE Logs which are intended for the user (which Monero was using a lot through epee, but really isn't a nice way to go things) should use the "global" category. There are a few helper macros for using this category, eg: MGINFO("this shows up by default") or MGINFO_RED("this is red"), to try to keep a similar look and feel for now. Existing epee log macros still exist, and map to the new log levels, but since they're used as a "user facing" UI element as much as a logging system, they often don't map well to log severities (ie, a log level 0 log may be an error, or may be something we want the user to see, such as an important info). In those cases, I tried to use the new macros. In other cases, I left the existing macros in. When modifying logs, it is probably best to switch to the new macros with explicit levels. The --log-level options and set_log commands now also accept category settings, in addition to the epee style log levels.
2017-01-01 17:34:23 +01:00
MINFO(tools::wallet_rpc_server::tr("Storing wallet..."));
wal->store();
Change logging to easylogging++ This replaces the epee and data_loggers logging systems with a single one, and also adds filename:line and explicit severity levels. Categories may be defined, and logging severity set by category (or set of categories). epee style 0-4 log level maps to a sensible severity configuration. Log files now also rotate when reaching 100 MB. To select which logs to output, use the MONERO_LOGS environment variable, with a comma separated list of categories (globs are supported), with their requested severity level after a colon. If a log matches more than one such setting, the last one in the configuration string applies. A few examples: This one is (mostly) silent, only outputting fatal errors: MONERO_LOGS=*:FATAL This one is very verbose: MONERO_LOGS=*:TRACE This one is totally silent (logwise): MONERO_LOGS="" This one outputs all errors and warnings, except for the "verify" category, which prints just fatal errors (the verify category is used for logs about incoming transactions and blocks, and it is expected that some/many will fail to verify, hence we don't want the spam): MONERO_LOGS=*:WARNING,verify:FATAL Log levels are, in decreasing order of priority: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE Subcategories may be added using prefixes and globs. This example will output net.p2p logs at the TRACE level, but all other net* logs only at INFO: MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE Logs which are intended for the user (which Monero was using a lot through epee, but really isn't a nice way to go things) should use the "global" category. There are a few helper macros for using this category, eg: MGINFO("this shows up by default") or MGINFO_RED("this is red"), to try to keep a similar look and feel for now. Existing epee log macros still exist, and map to the new log levels, but since they're used as a "user facing" UI element as much as a logging system, they often don't map well to log severities (ie, a log level 0 log may be an error, or may be something we want the user to see, such as an important info). In those cases, I tried to use the new macros. In other cases, I left the existing macros in. When modifying logs, it is probably best to switch to the new macros with explicit levels. The --log-level options and set_log commands now also accept category settings, in addition to the epee style log levels.
2017-01-01 17:34:23 +01:00
MINFO(tools::wallet_rpc_server::tr("Stored ok"));
return 1;
}
Change logging to easylogging++ This replaces the epee and data_loggers logging systems with a single one, and also adds filename:line and explicit severity levels. Categories may be defined, and logging severity set by category (or set of categories). epee style 0-4 log level maps to a sensible severity configuration. Log files now also rotate when reaching 100 MB. To select which logs to output, use the MONERO_LOGS environment variable, with a comma separated list of categories (globs are supported), with their requested severity level after a colon. If a log matches more than one such setting, the last one in the configuration string applies. A few examples: This one is (mostly) silent, only outputting fatal errors: MONERO_LOGS=*:FATAL This one is very verbose: MONERO_LOGS=*:TRACE This one is totally silent (logwise): MONERO_LOGS="" This one outputs all errors and warnings, except for the "verify" category, which prints just fatal errors (the verify category is used for logs about incoming transactions and blocks, and it is expected that some/many will fail to verify, hence we don't want the spam): MONERO_LOGS=*:WARNING,verify:FATAL Log levels are, in decreasing order of priority: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE Subcategories may be added using prefixes and globs. This example will output net.p2p logs at the TRACE level, but all other net* logs only at INFO: MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE Logs which are intended for the user (which Monero was using a lot through epee, but really isn't a nice way to go things) should use the "global" category. There are a few helper macros for using this category, eg: MGINFO("this shows up by default") or MGINFO_RED("this is red"), to try to keep a similar look and feel for now. Existing epee log macros still exist, and map to the new log levels, but since they're used as a "user facing" UI element as much as a logging system, they often don't map well to log severities (ie, a log level 0 log may be an error, or may be something we want the user to see, such as an important info). In those cases, I tried to use the new macros. In other cases, I left the existing macros in. When modifying logs, it is probably best to switch to the new macros with explicit levels. The --log-level options and set_log commands now also accept category settings, in addition to the epee style log levels.
2017-01-01 17:34:23 +01:00
MINFO(tools::wallet_rpc_server::tr("Loaded ok"));
}
catch (const std::exception& e)
{
LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
return 1;
}
just_dir:
tools::wallet_rpc_server wrpc;
if (wal) wrpc.set_wallet(wal.release());
bool r = wrpc.init(&(vm.get()));
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"));
try
{
wrpc.run();
}
catch (const std::exception &e)
{
LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
return 1;
}
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet rpc server"));
try
{
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet..."));
wrpc.stop();
Change logging to easylogging++ This replaces the epee and data_loggers logging systems with a single one, and also adds filename:line and explicit severity levels. Categories may be defined, and logging severity set by category (or set of categories). epee style 0-4 log level maps to a sensible severity configuration. Log files now also rotate when reaching 100 MB. To select which logs to output, use the MONERO_LOGS environment variable, with a comma separated list of categories (globs are supported), with their requested severity level after a colon. If a log matches more than one such setting, the last one in the configuration string applies. A few examples: This one is (mostly) silent, only outputting fatal errors: MONERO_LOGS=*:FATAL This one is very verbose: MONERO_LOGS=*:TRACE This one is totally silent (logwise): MONERO_LOGS="" This one outputs all errors and warnings, except for the "verify" category, which prints just fatal errors (the verify category is used for logs about incoming transactions and blocks, and it is expected that some/many will fail to verify, hence we don't want the spam): MONERO_LOGS=*:WARNING,verify:FATAL Log levels are, in decreasing order of priority: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE Subcategories may be added using prefixes and globs. This example will output net.p2p logs at the TRACE level, but all other net* logs only at INFO: MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE Logs which are intended for the user (which Monero was using a lot through epee, but really isn't a nice way to go things) should use the "global" category. There are a few helper macros for using this category, eg: MGINFO("this shows up by default") or MGINFO_RED("this is red"), to try to keep a similar look and feel for now. Existing epee log macros still exist, and map to the new log levels, but since they're used as a "user facing" UI element as much as a logging system, they often don't map well to log severities (ie, a log level 0 log may be an error, or may be something we want the user to see, such as an important info). In those cases, I tried to use the new macros. In other cases, I left the existing macros in. When modifying logs, it is probably best to switch to the new macros with explicit levels. The --log-level options and set_log commands now also accept category settings, in addition to the epee style log levels.
2017-01-01 17:34:23 +01:00
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stored ok"));
}
catch (const std::exception& e)
{
LOG_ERROR(tools::wallet_rpc_server::tr("Failed to store wallet: ") << e.what());
return 1;
}
return 0;
}