wallet, simplewallet: Drop support for mine-to-use RPC system

Using post-PR wallet with pre-PR node will generate error message specific to this PR and not generic "command failed" mesages.
This commit is contained in:
Jeffrey Ryan 2023-01-28 01:20:13 -06:00 committed by jeffro256
parent 5e19f232d0
commit d2a591d7c6
13 changed files with 32 additions and 1022 deletions

View File

@ -64,7 +64,6 @@
#include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/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 "rpc/rpc_payment_signature.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"
@ -105,15 +104,12 @@ typedef cryptonote::simple_wallet sw;
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); \
/* stop any background refresh and other processes, and take over */ \ /* stop any background refresh and other processes, and take over */ \
m_suspend_rpc_payment_mining.store(true, std::memory_order_relaxed); \
m_wallet->stop(); \ m_wallet->stop(); \
boost::unique_lock<boost::mutex> lock(m_idle_mutex); \ boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
m_idle_cond.notify_all(); \ m_idle_cond.notify_all(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
/* m_idle_mutex is still locked here */ \ /* m_idle_mutex is still locked here */ \
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
m_suspend_rpc_payment_mining.store(false, std::memory_order_relaxed); \
m_rpc_payment_checker.trigger(); \
m_idle_cond.notify_one(); \ m_idle_cond.notify_one(); \
}) })
@ -137,9 +133,6 @@ typedef cryptonote::simple_wallet sw;
#define REFRESH_PERIOD 90 // seconds #define REFRESH_PERIOD 90 // seconds
#define CREDITS_TARGET 50000
#define MAX_PAYMENT_DIFF 10000
#define MIN_PAYMENT_RATE 0.01f // per hash
#define MAX_MNEW_ADDRESSES 1000 #define MAX_MNEW_ADDRESSES 1000
#define CHECK_MULTISIG_ENABLED() \ #define CHECK_MULTISIG_ENABLED() \
@ -167,7 +160,6 @@ namespace
{ {
const std::array<const char* const, 5> allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}}; const std::array<const char* const, 5> allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}};
const auto arg_wallet_file = wallet_args::arg_wallet_file(); const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
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_device = {"generate-from-device", sw::tr("Generate new wallet from device and save it to <arg>"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_device = {"generate-from-device", sw::tr("Generate new wallet from device 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"), ""};
@ -283,9 +275,6 @@ namespace
const char* USAGE_NET_STATS("net_stats"); const char* USAGE_NET_STATS("net_stats");
const char* USAGE_PUBLIC_NODES("public_nodes"); const char* USAGE_PUBLIC_NODES("public_nodes");
const char* USAGE_WELCOME("welcome"); const char* USAGE_WELCOME("welcome");
const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info");
const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc [<number_of_threads>]");
const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc");
const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]"); const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]");
const char* USAGE_VERSION("version"); const char* USAGE_VERSION("version");
const char* USAGE_HELP("help [<command> | all]"); const char* USAGE_HELP("help [<command> | all]");
@ -540,10 +529,9 @@ void simple_wallet::handle_transfer_exception(const std::exception_ptr &e, bool
{ {
std::rethrow_exception(e); std::rethrow_exception(e);
} }
catch (const tools::error::payment_required&) catch (const tools::error::deprecated_rpc_access&)
{ {
fail_msg_writer() << tr("Payment required, see the 'rpc_payment_info' command"); fail_msg_writer() << tr("Daemon requires deprecated RPC payment. See https://github.com/monero-project/monero/issues/8722");
m_need_payment = true;
} }
catch (const tools::error::no_connection_to_daemon&) catch (const tools::error::no_connection_to_daemon&)
{ {
@ -1924,77 +1912,6 @@ bool simple_wallet::unset_ring(const std::vector<std::string> &args)
return true; return true;
} }
bool simple_wallet::rpc_payment_info(const std::vector<std::string> &args)
{
if (!try_connect_to_daemon())
return true;
LOCK_IDLE_SCOPE();
try
{
bool payment_required;
uint64_t credits, diff, credits_per_hash_found, height, seed_height;
uint32_t cookie;
std::string hashing_blob;
crypto::hash seed_hash, next_seed_hash;
crypto::public_key pkey;
crypto::secret_key_to_public_key(m_wallet->get_rpc_client_secret_key(), pkey);
message_writer() << tr("RPC client ID: ") << pkey;
message_writer() << tr("RPC client secret key: ") << m_wallet->get_rpc_client_secret_key();
if (!m_wallet->get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie))
{
fail_msg_writer() << tr("Failed to query daemon");
return true;
}
if (payment_required)
{
uint64_t target = m_wallet->credits_target();
if (target == 0)
target = CREDITS_TARGET;
message_writer() << tr("Using daemon: ") << m_wallet->get_daemon_address();
message_writer() << tr("Payments required for node use, current credits: ") << credits;
message_writer() << tr("Credits target: ") << target;
uint64_t expected, discrepancy;
m_wallet->credit_report(expected, discrepancy);
message_writer() << tr("Credits spent this session: ") << expected;
if (expected)
message_writer() << tr("Credit discrepancy this session: ") << discrepancy << " (" << 100.0f * discrepancy / expected << "%)";
float cph = credits_per_hash_found / (float)diff;
message_writer() << tr("Difficulty: ") << diff << ", " << credits_per_hash_found << " " << tr("credits per hash found, ") << cph << " " << tr("credits/hash");
const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
bool mining = (now - m_last_rpc_payment_mining_time).total_microseconds() < 1000000;
if (mining)
{
float hash_rate = m_rpc_payment_hash_rate;
if (hash_rate > 0)
{
message_writer() << (boost::format(tr("Mining for payment at %.1f H/s")) % hash_rate).str();
if (credits < target)
{
std::chrono::seconds seconds((unsigned)((target - credits) / cph / hash_rate));
std::string target_string = get_human_readable_timespan(seconds);
message_writer() << (boost::format(tr("Estimated time till %u credits target mined: %s")) % target % target_string).str();
}
}
else
message_writer() << tr("Mining for payment");
}
else
message_writer() << tr("Not mining");
}
else
message_writer() << tr("No payment needed for node use");
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
fail_msg_writer() << tr("unexpected error: ") << e.what();
}
return true;
}
bool simple_wallet::blackball(const std::vector<std::string> &args) bool simple_wallet::blackball(const std::vector<std::string> &args)
{ {
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets; uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
@ -2242,31 +2159,19 @@ bool simple_wallet::public_nodes(const std::vector<std::string> &args)
try try
{ {
auto nodes = m_wallet->get_public_nodes(false); auto nodes = m_wallet->get_public_nodes(false);
m_claimed_cph.clear();
if (nodes.empty()) if (nodes.empty())
{ {
fail_msg_writer() << tr("No known public nodes"); fail_msg_writer() << tr("No known public nodes");
return true; return true;
} }
std::sort(nodes.begin(), nodes.end(), [](const public_node &node0, const public_node &node1) {
if (node0.rpc_credits_per_hash && node1.rpc_credits_per_hash == 0)
return true;
if (node0.rpc_credits_per_hash && node1.rpc_credits_per_hash)
return node0.rpc_credits_per_hash < node1.rpc_credits_per_hash;
return false;
});
const uint64_t now = time(NULL); const uint64_t now = time(NULL);
message_writer() << boost::format("%32s %12s %16s") % tr("address") % tr("credits/hash") % tr("last_seen"); message_writer() << boost::format("%32s %16s") % tr("address") % tr("last_seen");
for (const auto &node: nodes) for (const auto &node: nodes)
{ {
const float cph = node.rpc_credits_per_hash / RPC_CREDITS_PER_HASH_SCALE;
char cphs[9];
snprintf(cphs, sizeof(cphs), "%.3f", cph);
const std::string last_seen = node.last_seen == 0 ? tr("never") : get_human_readable_timespan(std::chrono::seconds(now - node.last_seen)); const std::string last_seen = node.last_seen == 0 ? tr("never") : get_human_readable_timespan(std::chrono::seconds(now - node.last_seen));
std::string host = node.host + ":" + std::to_string(node.rpc_port); std::string host = node.host + ":" + std::to_string(node.rpc_port);
message_writer() << boost::format("%32s %12s %16s") % host % cphs % last_seen; message_writer() << boost::format("%32s %16s") % host % last_seen;
m_claimed_cph[host] = node.rpc_credits_per_hash;
} }
} }
catch (const std::exception &e) catch (const std::exception &e)
@ -2341,68 +2246,6 @@ bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>&
return m_wallet->import_key_images(exported_txs, 0, true); return m_wallet->import_key_images(exported_txs, 0, true);
} }
bool simple_wallet::start_mining_for_rpc(const std::vector<std::string> &args)
{
if (!try_connect_to_daemon())
return true;
bool ok = true;
if(args.size() >= 1)
{
uint16_t num = 0;
ok = string_tools::get_xtype_from_string(num, args[0]);
m_rpc_payment_threads = num;
}
else
{
m_rpc_payment_threads = 0;
}
if (!ok)
{
PRINT_USAGE(USAGE_START_MINING_FOR_RPC);
return true;
}
LOCK_IDLE_SCOPE();
bool payment_required;
uint64_t credits, diff, credits_per_hash_found, height, seed_height;
uint32_t cookie;
std::string hashing_blob;
crypto::hash seed_hash, next_seed_hash;
if (!m_wallet->get_rpc_payment_info(true, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie))
{
fail_msg_writer() << tr("Failed to query daemon");
return true;
}
if (!payment_required)
{
fail_msg_writer() << tr("Daemon does not require payment for RPC access");
return true;
}
m_rpc_payment_mining_requested = true;
m_rpc_payment_checker.trigger();
const float cph = credits_per_hash_found / (float)diff;
bool low = (diff > MAX_PAYMENT_DIFF || cph < MIN_PAYMENT_RATE);
success_msg_writer() << (boost::format(tr("Starting mining for RPC access: diff %llu, %f credits/hash%s")) % diff % cph % (low ? " - this is low" : "")).str();
success_msg_writer() << tr("Run stop_mining_for_rpc to stop");
return true;
}
bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args)
{
if (!try_connect_to_daemon())
return true;
LOCK_IDLE_SCOPE();
m_rpc_payment_mining_requested = false;
m_last_rpc_payment_mining_time = boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1));
m_rpc_payment_hash_rate = -1.0f;
return true;
}
bool simple_wallet::show_qr_code(const std::vector<std::string> &args) bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
{ {
uint32_t subaddress_index = 0; uint32_t subaddress_index = 0;
@ -2812,53 +2655,6 @@ bool simple_wallet::set_segregate_pre_fork_outputs(const std::vector<std::string
return true; return true;
} }
bool simple_wallet::set_persistent_rpc_client_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
parse_bool_and_use(args[1], [&](bool r) {
m_wallet->persistent_rpc_client_id(r);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
});
}
return true;
}
bool simple_wallet::set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
float threshold;
if (!epee::string_tools::get_xtype_from_string(threshold, args[1]) || threshold < 0.0f)
{
fail_msg_writer() << tr("Invalid threshold");
return true;
}
m_wallet->auto_mine_for_rpc_payment_threshold(threshold);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
}
return true;
}
bool simple_wallet::set_credits_target(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
uint64_t target;
if (!epee::string_tools::get_xtype_from_string(target, args[1]))
{
fail_msg_writer() << tr("Invalid target");
return true;
}
m_wallet->credits_target(target);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
}
return true;
}
bool simple_wallet::set_key_reuse_mitigation2(const std::vector<std::string> &args/* = std::vector<std::string>()*/) bool simple_wallet::set_key_reuse_mitigation2(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{ {
const auto pwd_container = get_and_verify_password(); const auto pwd_container = get_and_verify_password();
@ -3246,12 +3042,6 @@ simple_wallet::simple_wallet()
, m_last_activity_time(time(NULL)) , m_last_activity_time(time(NULL))
, m_locked(false) , m_locked(false)
, m_in_command(false) , m_in_command(false)
, m_need_payment(false)
, m_rpc_payment_mining_requested(false)
, m_last_rpc_payment_mining_time(boost::gregorian::date(1970, 1, 1))
, m_daemon_rpc_payment_message_displayed(false)
, m_rpc_payment_hash_rate(-1.0f)
, m_suspend_rpc_payment_mining(false)
{ {
m_cmd_binder.set_handler("start_mining", m_cmd_binder.set_handler("start_mining",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining, _1),
@ -3433,12 +3223,6 @@ simple_wallet::simple_wallet()
" Device name for hardware wallet.\n " " Device name for hardware wallet.\n "
"export-format <\"binary\"|\"ascii\">\n " "export-format <\"binary\"|\"ascii\">\n "
" Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n " " Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n "
"persistent-rpc-client-id <1|0>\n "
" Whether to keep using the same client id for RPC payment over wallet restarts.\n"
"auto-mine-for-rpc-payment-threshold <float>\n "
" Whether to automatically start mining for RPC payment if the daemon requires it.\n"
"credits-target <unsigned int>\n"
" The RPC payment credits balance to target (0 for default).\n "
"show-wallet-name-when-locked <1|0>\n " "show-wallet-name-when-locked <1|0>\n "
" Set this if you would like to display the wallet name when locked.\n " " Set this if you would like to display the wallet name when locked.\n "
"enable-multisig-experimental <1|0>\n " "enable-multisig-experimental <1|0>\n "
@ -3759,18 +3543,6 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::on_command, this, &simple_wallet::version, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::version, _1),
tr(USAGE_VERSION), tr(USAGE_VERSION),
tr("Returns version information")); tr("Returns version information"));
m_cmd_binder.set_handler("rpc_payment_info",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::rpc_payment_info, _1),
tr(USAGE_RPC_PAYMENT_INFO),
tr("Get info about RPC payments to current node"));
m_cmd_binder.set_handler("start_mining_for_rpc",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining_for_rpc, _1),
tr(USAGE_START_MINING_FOR_RPC),
tr("Start mining to pay for RPC access"));
m_cmd_binder.set_handler("stop_mining_for_rpc",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::stop_mining_for_rpc, _1),
tr(USAGE_STOP_MINING_FOR_RPC),
tr("Stop mining to pay for RPC access"));
m_cmd_binder.set_handler("show_qr_code", m_cmd_binder.set_handler("show_qr_code",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_qr_code, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_qr_code, _1),
tr(USAGE_SHOW_QR_CODE), tr(USAGE_SHOW_QR_CODE),
@ -3854,9 +3626,6 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
<< " (disabled on Windows)" << " (disabled on Windows)"
#endif #endif
; ;
success_msg_writer() << "persistent-rpc-client-id = " << m_wallet->persistent_rpc_client_id();
success_msg_writer() << "auto-mine-for-rpc-payment-threshold = " << m_wallet->auto_mine_for_rpc_payment_threshold();
success_msg_writer() << "credits-target = " << m_wallet->credits_target();
success_msg_writer() << "load-deprecated-formats = " << m_wallet->load_deprecated_formats(); success_msg_writer() << "load-deprecated-formats = " << m_wallet->load_deprecated_formats();
success_msg_writer() << "enable-multisig-experimental = " << m_wallet->is_multisig_enabled(); success_msg_writer() << "enable-multisig-experimental = " << m_wallet->is_multisig_enabled();
return true; return true;
@ -3922,9 +3691,6 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>")); CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
CHECK_SIMPLE_VARIABLE("export-format", set_export_format, tr("\"binary\" or \"ascii\"")); CHECK_SIMPLE_VARIABLE("export-format", set_export_format, tr("\"binary\" or \"ascii\""));
CHECK_SIMPLE_VARIABLE("load-deprecated-formats", set_load_deprecated_formats, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("load-deprecated-formats", set_load_deprecated_formats, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("persistent-rpc-client-id", set_persistent_rpc_client_id, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("auto-mine-for-rpc-payment-threshold", set_auto_mine_for_rpc_payment_threshold, tr("floating point >= 0"));
CHECK_SIMPLE_VARIABLE("credits-target", set_credits_target, tr("unsigned integer"));
CHECK_SIMPLE_VARIABLE("enable-multisig-experimental", set_enable_multisig, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("enable-multisig-experimental", set_enable_multisig, tr("0 or 1"));
} }
fail_msg_writer() << tr("set: unrecognized argument(s)"); fail_msg_writer() << tr("set: unrecognized argument(s)");
@ -4694,17 +4460,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false; return false;
} }
if (!command_line::is_arg_defaulted(vm, arg_rpc_client_secret_key))
{
crypto::secret_key rpc_client_secret_key;
if (!epee::string_tools::hex_to_pod(command_line::get_arg(vm, arg_rpc_client_secret_key), rpc_client_secret_key))
{
fail_msg_writer() << tr("RPC client secret key should be 32 byte in hex format");
return false;
}
m_wallet->set_rpc_client_secret_key(rpc_client_secret_key);
}
if (!m_wallet->is_trusted_daemon()) if (!m_wallet->is_trusted_daemon())
{ {
message_writer(console_color_red, true) << (boost::format(tr("Warning: using an untrusted daemon at %s")) % m_wallet->get_daemon_address()).str(); message_writer(console_color_red, true) << (boost::format(tr("Warning: using an untrusted daemon at %s")) % m_wallet->get_daemon_address()).str();
@ -5243,7 +4998,6 @@ bool simple_wallet::close_wallet()
if (m_idle_run.load(std::memory_order_relaxed)) if (m_idle_run.load(std::memory_order_relaxed))
{ {
m_idle_run.store(false, std::memory_order_relaxed); m_idle_run.store(false, std::memory_order_relaxed);
m_suspend_rpc_payment_mining.store(true, std::memory_order_relaxed);
m_wallet->stop(); m_wallet->stop();
{ {
boost::unique_lock<boost::mutex> lock(m_idle_mutex); boost::unique_lock<boost::mutex> lock(m_idle_mutex);
@ -5519,40 +5273,6 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::check_daemon_rpc_prices(const std::string &daemon_url, uint32_t &actual_cph, uint32_t &claimed_cph)
{
try
{
auto i = m_claimed_cph.find(daemon_url);
if (i == m_claimed_cph.end())
return false;
claimed_cph = m_claimed_cph[daemon_url];
bool payment_required;
uint64_t credits, diff, credits_per_hash_found, height, seed_height;
uint32_t cookie;
cryptonote::blobdata hashing_blob;
crypto::hash seed_hash, next_seed_hash;
if (m_wallet->get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required)
{
actual_cph = RPC_CREDITS_PER_HASH_SCALE * (credits_per_hash_found / (float)diff);
return true;
}
else
{
fail_msg_writer() << tr("Error checking daemon RPC access prices");
}
}
catch (const std::exception &e)
{
// can't check
fail_msg_writer() << tr("Error checking daemon RPC access prices: ") << e.what();
return false;
}
// no record found for this daemon
return false;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::set_daemon(const std::vector<std::string>& args) bool simple_wallet::set_daemon(const std::vector<std::string>& args)
{ {
std::string daemon_url; std::string daemon_url;
@ -5647,20 +5367,6 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
} }
success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ? tr("trusted") : tr("untrusted")); success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ? tr("trusted") : tr("untrusted"));
// check whether the daemon's prices match the claim, and disconnect if not, to disincentivize daemons lying
uint32_t actual_cph, claimed_cph;
if (check_daemon_rpc_prices(daemon_url, actual_cph, claimed_cph))
{
if (actual_cph < claimed_cph)
{
fail_msg_writer() << tr("Daemon RPC credits/hash is less than was claimed. Either this daemon is cheating, or it changed its setup recently.");
fail_msg_writer() << tr("Claimed: ") << claimed_cph / (float)RPC_CREDITS_PER_HASH_SCALE;
fail_msg_writer() << tr("Actual: ") << actual_cph / (float)RPC_CREDITS_PER_HASH_SCALE;
}
}
m_daemon_rpc_payment_message_displayed = false;
} else { } else {
fail_msg_writer() << tr("This does not seem to be a valid daemon URL."); fail_msg_writer() << tr("This does not seem to be a valid daemon URL.");
} }
@ -5927,10 +5633,9 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
{ {
ss << tr("no connection to daemon. Please make sure daemon is running."); ss << tr("no connection to daemon. Please make sure daemon is running.");
} }
catch (const tools::error::payment_required&) catch (const tools::error::deprecated_rpc_access&)
{ {
ss << tr("payment required."); ss << tr("Daemon requires deprecated RPC payment. See https://github.com/monero-project/monero/issues/8722");
m_need_payment = true;
} }
catch (const tools::error::wallet_rpc_error& e) catch (const tools::error::wallet_rpc_error& e)
{ {
@ -6266,10 +5971,9 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
{ {
fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running."); fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
} }
catch (const tools::error::payment_required&) catch (const tools::error::deprecated_rpc_access&)
{ {
fail_msg_writer() << tr("payment required."); fail_msg_writer() << tr("Daemon requires deprecated RPC payment. See https://github.com/monero-project/monero/issues/8722");
m_need_payment = true;
} }
catch (const tools::error::is_key_image_spent_error&) catch (const tools::error::is_key_image_spent_error&)
{ {
@ -6380,7 +6084,6 @@ bool simple_wallet::process_ring_members(const std::vector<tools::wallet2::pendi
} }
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
req.get_txid = true; req.get_txid = true;
req.client = cryptonote::make_rpc_payment_signature(m_wallet->get_rpc_client_secret_key());
bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res); bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
err = interpret_rpc_response(r, res.status); err = interpret_rpc_response(r, res.status);
if (!err.empty()) if (!err.empty())
@ -9276,7 +8979,6 @@ void simple_wallet::wallet_idle_thread()
#endif #endif
m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this)); m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this));
m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this)); m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this));
m_rpc_payment_checker.do_call(boost::bind(&simple_wallet::check_rpc_payment, this));
if (!m_idle_run.load(std::memory_order_relaxed)) if (!m_idle_run.load(std::memory_order_relaxed))
break; break;
@ -9336,78 +9038,6 @@ bool simple_wallet::check_mms()
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::check_rpc_payment()
{
if (!m_rpc_payment_mining_requested && m_wallet->auto_mine_for_rpc_payment_threshold() == 0.0f)
return true;
uint64_t target = m_wallet->credits_target();
if (target == 0)
target = CREDITS_TARGET;
if (m_rpc_payment_mining_requested)
target = std::numeric_limits<uint64_t>::max();
bool need_payment = m_need_payment || m_rpc_payment_mining_requested || (m_wallet->credits() < target && m_wallet->daemon_requires_payment());
if (need_payment)
{
const boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::universal_time();
auto startfunc = [this](uint64_t diff, uint64_t credits_per_hash_found)
{
const float cph = credits_per_hash_found / (float)diff;
bool low = (diff > MAX_PAYMENT_DIFF || cph < MIN_PAYMENT_RATE);
if (credits_per_hash_found > 0 && cph >= m_wallet->auto_mine_for_rpc_payment_threshold())
{
MINFO(std::to_string(cph) << " credits per hash is >= our threshold (" << m_wallet->auto_mine_for_rpc_payment_threshold() << "), starting mining");
return true;
}
else if (m_rpc_payment_mining_requested)
{
MINFO("Mining for RPC payment was requested, starting mining");
return true;
}
else
{
if (!m_daemon_rpc_payment_message_displayed)
{
success_msg_writer() << boost::format(tr("Daemon requests payment at diff %llu, with %f credits/hash%s. Run start_mining_for_rpc to start mining to pay for RPC access, or use another daemon")) %
diff % cph % (low ? " - this is low" : "");
m_cmd_binder.print_prompt();
m_daemon_rpc_payment_message_displayed = true;
}
return false;
}
};
auto contfunc = [&,this](unsigned n_hashes)
{
if (m_suspend_rpc_payment_mining.load(std::memory_order_relaxed))
return false;
const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
m_last_rpc_payment_mining_time = now;
if ((now - start_time).total_microseconds() >= 2 * 1000000)
m_rpc_payment_hash_rate = n_hashes / (float)((now - start_time).total_seconds());
if ((now - start_time).total_microseconds() >= REFRESH_PERIOD * 1000000)
return false;
return true;
};
auto foundfunc = [this, target](uint64_t credits)
{
m_need_payment = false;
return credits < target;
};
auto errorfunc = [this](const std::string &error)
{
fail_msg_writer() << tr("Error mining to daemon: ") << error;
m_cmd_binder.print_prompt();
};
bool ret = m_wallet->search_for_rpc_payment(target, m_rpc_payment_threads, startfunc, contfunc, foundfunc, errorfunc);
if (!ret)
{
fail_msg_writer() << tr("Failed to start mining for RPC payment");
m_cmd_binder.print_prompt();
}
}
return true;
}
//----------------------------------------------------------------------------------------------------
std::string simple_wallet::get_prompt() const std::string simple_wallet::get_prompt() const
{ {
if (m_locked) if (m_locked)
@ -10661,7 +10291,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_create_address_file); command_line::add_arg(desc_params, arg_create_address_file);
command_line::add_arg(desc_params, arg_subaddress_lookahead); command_line::add_arg(desc_params, arg_subaddress_lookahead);
command_line::add_arg(desc_params, arg_use_english_language_names); command_line::add_arg(desc_params, arg_use_english_language_names);
command_line::add_arg(desc_params, arg_rpc_client_secret_key);
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);

View File

@ -154,9 +154,6 @@ namespace cryptonote
bool set_export_format(const std::vector<std::string> &args = std::vector<std::string>()); bool set_export_format(const std::vector<std::string> &args = std::vector<std::string>());
bool set_load_deprecated_formats(const std::vector<std::string> &args = std::vector<std::string>()); bool set_load_deprecated_formats(const std::vector<std::string> &args = std::vector<std::string>());
bool set_enable_multisig(const std::vector<std::string> &args = std::vector<std::string>()); bool set_enable_multisig(const std::vector<std::string> &args = std::vector<std::string>());
bool set_persistent_rpc_client_id(const std::vector<std::string> &args = std::vector<std::string>());
bool set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args = std::vector<std::string>());
bool set_credits_target(const std::vector<std::string> &args = std::vector<std::string>());
bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool apropos(const std::vector<std::string> &args); bool apropos(const std::vector<std::string> &args);
bool scan_tx(const std::vector<std::string> &args); bool scan_tx(const std::vector<std::string> &args);
@ -258,9 +255,6 @@ namespace cryptonote
bool thaw(const std::vector<std::string>& args); bool thaw(const std::vector<std::string>& args);
bool frozen(const std::vector<std::string>& args); bool frozen(const std::vector<std::string>& args);
bool lock(const std::vector<std::string>& args); bool lock(const std::vector<std::string>& args);
bool rpc_payment_info(const std::vector<std::string> &args);
bool start_mining_for_rpc(const std::vector<std::string> &args);
bool stop_mining_for_rpc(const std::vector<std::string> &args);
bool show_qr_code(const std::vector<std::string> &args); bool show_qr_code(const std::vector<std::string> &args);
bool net_stats(const std::vector<std::string>& args); bool net_stats(const std::vector<std::string>& args);
bool public_nodes(const std::vector<std::string>& args); bool public_nodes(const std::vector<std::string>& args);
@ -338,7 +332,6 @@ namespace cryptonote
bool check_inactivity(); bool check_inactivity();
bool check_refresh(); bool check_refresh();
bool check_mms(); bool check_mms();
bool check_rpc_payment();
void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon); void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon);
@ -458,17 +451,6 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<1> m_inactivity_checker; epee::math_helper::once_a_time_seconds<1> m_inactivity_checker;
epee::math_helper::once_a_time_seconds_range<get_random_interval<80 * 1000000, 100 * 1000000>> m_refresh_checker; epee::math_helper::once_a_time_seconds_range<get_random_interval<80 * 1000000, 100 * 1000000>> m_refresh_checker;
epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 110 * 1000000>> m_mms_checker; epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 110 * 1000000>> m_mms_checker;
epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 115 * 1000000>> m_rpc_payment_checker;
std::atomic<bool> m_need_payment;
boost::posix_time::ptime m_last_rpc_payment_mining_time;
bool m_rpc_payment_mining_requested;
uint32_t m_rpc_payment_threads = 0;
bool m_daemon_rpc_payment_message_displayed;
float m_rpc_payment_hash_rate;
std::atomic<bool> m_suspend_rpc_payment_mining;
std::unordered_map<std::string, uint32_t> m_claimed_cph;
// MMS // MMS
mms::message_store& get_message_store() const { return m_wallet->get_message_store(); }; mms::message_store& get_message_store() const { return m_wallet->get_message_store(); };

View File

@ -37,7 +37,6 @@ set(wallet_sources
node_rpc_proxy.cpp node_rpc_proxy.cpp
message_store.cpp message_store.cpp
message_transporter.cpp message_transporter.cpp
wallet_rpc_payments.cpp
) )
monero_find_all_headers(wallet_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") monero_find_all_headers(wallet_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")

View File

@ -28,8 +28,6 @@
#include "node_rpc_proxy.h" #include "node_rpc_proxy.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "rpc/rpc_payment_signature.h"
#include "rpc/rpc_payment_costs.h"
#include "storages/http_abstract_invoke.h" #include "storages/http_abstract_invoke.h"
#include <boost/thread.hpp> #include <boost/thread.hpp>
@ -37,7 +35,6 @@
#define RETURN_ON_RPC_RESPONSE_ERROR(r, error, res, method) \ #define RETURN_ON_RPC_RESPONSE_ERROR(r, error, res, method) \
do { \ do { \
CHECK_AND_ASSERT_MES(error.code == 0, error.message, error.message); \ CHECK_AND_ASSERT_MES(error.code == 0, error.message, error.message); \
handle_payment_changes(res, std::integral_constant<bool, HasCredits<decltype(res)>::Has>()); \
CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); \ CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); \
/* empty string -> not connection */ \ /* empty string -> not connection */ \
CHECK_AND_ASSERT_MES(!res.status.empty(), res.status, "No connection to daemon"); \ CHECK_AND_ASSERT_MES(!res.status.empty(), res.status, "No connection to daemon"); \
@ -53,9 +50,8 @@ namespace tools
static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex) NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, boost::recursive_mutex &mutex)
: m_http_client(http_client) : m_http_client(http_client)
, m_rpc_payment_state(rpc_payment_state)
, m_daemon_rpc_mutex(mutex) , m_daemon_rpc_mutex(mutex)
, m_offline(false) , m_offline(false)
{ {
@ -77,16 +73,8 @@ void NodeRPCProxy::invalidate()
m_block_weight_limit = 0; m_block_weight_limit = 0;
m_adjusted_time = 0; m_adjusted_time = 0;
m_get_info_time = 0; m_get_info_time = 0;
m_rpc_payment_info_time = 0;
m_rpc_payment_seed_height = 0;
m_rpc_payment_seed_hash = crypto::null_hash;
m_rpc_payment_next_seed_hash = crypto::null_hash;
m_height_time = 0; m_height_time = 0;
m_target_height_time = 0; m_target_height_time = 0;
m_rpc_payment_diff = 0;
m_rpc_payment_credits_per_hash_found = 0;
m_rpc_payment_height = 0;
m_rpc_payment_cookie = 0;
m_daemon_hard_forks.clear(); m_daemon_hard_forks.clear();
} }
@ -147,11 +135,8 @@ boost::optional<std::string> NodeRPCProxy::get_info()
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client, rpc_timeout);
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_info"); RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_info");
check_rpc_cost(m_rpc_payment_state, "get_info", resp_t.credits, pre_call_credits, COST_PER_GET_INFO);
} }
m_height = resp_t.height; m_height = resp_t.height;
@ -227,11 +212,8 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout);
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "hard_fork_info"); RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "hard_fork_info");
check_rpc_cost(m_rpc_payment_state, "hard_fork_info", resp_t.credits, pre_call_credits, COST_PER_HARD_FORK_INFO);
} }
m_earliest_height[version] = resp_t.earliest_height; m_earliest_height[version] = resp_t.earliest_height;
@ -259,11 +241,8 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate_2021_sc
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_fee_estimate"); RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_fee_estimate");
check_rpc_cost(m_rpc_payment_state, "get_fee_estimate", resp_t.credits, pre_call_credits, COST_PER_FEE_ESTIMATE);
} }
m_dynamic_base_fee_estimate = resp_t.fee; m_dynamic_base_fee_estimate = resp_t.fee;
@ -305,11 +284,8 @@ boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &f
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_fee_estimate"); RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_fee_estimate");
check_rpc_cost(m_rpc_payment_state, "get_fee_estimate", resp_t.credits, pre_call_credits, COST_PER_FEE_ESTIMATE);
} }
m_dynamic_base_fee_estimate = resp_t.fee; m_dynamic_base_fee_estimate = resp_t.fee;
@ -326,72 +302,6 @@ boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &f
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
boost::optional<std::string> NodeRPCProxy::get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie)
{
const time_t now = time(NULL);
if (m_rpc_payment_state.stale || now >= m_rpc_payment_info_time + 5*60 || (mining && now >= m_rpc_payment_info_time + 10)) // re-cache every 10 seconds if mining, 5 minutes otherwise
{
cryptonote::COMMAND_RPC_ACCESS_INFO::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_ACCESS_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "rpc_access_info", req_t, resp_t, m_http_client, rpc_timeout);
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "rpc_access_info");
m_rpc_payment_state.stale = false;
}
m_rpc_payment_diff = resp_t.diff;
m_rpc_payment_credits_per_hash_found = resp_t.credits_per_hash_found;
m_rpc_payment_height = resp_t.height;
m_rpc_payment_seed_height = resp_t.seed_height;
m_rpc_payment_cookie = resp_t.cookie;
if (m_rpc_payment_diff == 0)
{
// If no payment required daemon doesn't give us back a hashing blob
m_rpc_payment_blob.clear();
}
else if (!epee::string_tools::parse_hexstr_to_binbuff(resp_t.hashing_blob, m_rpc_payment_blob) || m_rpc_payment_blob.size() < 43)
{
MERROR("Invalid hashing blob: " << resp_t.hashing_blob);
return std::string("Invalid hashing blob");
}
if (resp_t.seed_hash.empty())
{
m_rpc_payment_seed_hash = crypto::null_hash;
}
else if (!epee::string_tools::hex_to_pod(resp_t.seed_hash, m_rpc_payment_seed_hash))
{
MERROR("Invalid seed_hash: " << resp_t.seed_hash);
return std::string("Invalid seed hash");
}
if (resp_t.next_seed_hash.empty())
{
m_rpc_payment_next_seed_hash = crypto::null_hash;
}
else if (!epee::string_tools::hex_to_pod(resp_t.next_seed_hash, m_rpc_payment_next_seed_hash))
{
MERROR("Invalid next_seed_hash: " << resp_t.next_seed_hash);
return std::string("Invalid next seed hash");
}
m_rpc_payment_info_time = now;
}
payment_required = m_rpc_payment_diff > 0;
credits = m_rpc_payment_state.credits;
diff = m_rpc_payment_diff;
credits_per_hash_found = m_rpc_payment_credits_per_hash_found;
blob = m_rpc_payment_blob;
height = m_rpc_payment_height;
seed_height = m_rpc_payment_seed_height;
seed_hash = m_rpc_payment_seed_hash;
next_seed_hash = m_rpc_payment_next_seed_hash;
cookie = m_rpc_payment_cookie;
return boost::none;
}
boost::optional<std::string> NodeRPCProxy::get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f) boost::optional<std::string> NodeRPCProxy::get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f)
{ {
const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp
@ -410,11 +320,7 @@ boost::optional<std::string> NodeRPCProxy::get_transactions(const std::vector<cr
bool r = false; bool r = false;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
r = net_utils::invoke_http_json("/gettransactions", req_t, resp_t, m_http_client, rpc_timeout); r = net_utils::invoke_http_json("/gettransactions", req_t, resp_t, m_http_client, rpc_timeout);
if (r && resp_t.status == CORE_RPC_STATUS_OK)
check_rpc_cost(m_rpc_payment_state, "/gettransactions", resp_t.credits, pre_call_credits, resp_t.txs.size() * COST_PER_TX);
} }
f(req_t, resp_t, r); f(req_t, resp_t, r);

View File

@ -34,7 +34,6 @@
#include "include_base_utils.h" #include "include_base_utils.h"
#include "net/abstract_http_client.h" #include "net/abstract_http_client.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "wallet_rpc_helpers.h"
namespace tools namespace tools
{ {
@ -42,9 +41,8 @@ namespace tools
class NodeRPCProxy class NodeRPCProxy
{ {
public: public:
NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex); NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, boost::recursive_mutex &mutex);
void set_client_secret_key(const crypto::secret_key &skey) { m_client_id_secret_key = skey; }
void invalidate(); void invalidate();
void set_offline(bool offline) { m_offline = offline; } void set_offline(bool offline) { m_offline = offline; }
@ -58,28 +56,13 @@ public:
boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee); boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee);
boost::optional<std::string> get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees); boost::optional<std::string> get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees);
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask); boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask);
boost::optional<std::string> get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
boost::optional<std::string> get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f); boost::optional<std::string> get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f);
private:
template<typename T> void handle_payment_changes(const T &res, std::true_type) {
if (res.status == CORE_RPC_STATUS_OK || res.status == CORE_RPC_STATUS_PAYMENT_REQUIRED)
m_rpc_payment_state.credits = res.credits;
if (res.top_hash != m_rpc_payment_state.top_hash)
{
m_rpc_payment_state.top_hash = res.top_hash;
m_rpc_payment_state.stale = true;
}
}
template<typename T> void handle_payment_changes(const T &res, std::false_type) {}
private: private:
boost::optional<std::string> get_info(); boost::optional<std::string> get_info();
epee::net_utils::http::abstract_http_client &m_http_client; epee::net_utils::http::abstract_http_client &m_http_client;
rpc_payment_state_t &m_rpc_payment_state;
boost::recursive_mutex &m_daemon_rpc_mutex; boost::recursive_mutex &m_daemon_rpc_mutex;
crypto::secret_key m_client_id_secret_key;
bool m_offline; bool m_offline;
uint64_t m_height; uint64_t m_height;
@ -94,15 +77,6 @@ private:
uint64_t m_target_height; uint64_t m_target_height;
uint64_t m_block_weight_limit; uint64_t m_block_weight_limit;
time_t m_get_info_time; time_t m_get_info_time;
time_t m_rpc_payment_info_time;
uint64_t m_rpc_payment_diff;
uint64_t m_rpc_payment_credits_per_hash_found;
cryptonote::blobdata m_rpc_payment_blob;
uint64_t m_rpc_payment_height;
uint64_t m_rpc_payment_seed_height;
crypto::hash m_rpc_payment_seed_hash;
crypto::hash m_rpc_payment_next_seed_hash;
uint32_t m_rpc_payment_cookie;
time_t m_height_time; time_t m_height_time;
time_t m_target_height_time; time_t m_target_height_time;
std::vector<std::pair<uint8_t, uint64_t>> m_daemon_hard_forks; std::vector<std::pair<uint8_t, uint64_t>> m_daemon_hard_forks;

View File

@ -51,15 +51,12 @@ using namespace epee;
#include "cryptonote_config.h" #include "cryptonote_config.h"
#include "hardforks/hardforks.h" #include "hardforks/hardforks.h"
#include "cryptonote_core/tx_sanity_check.h" #include "cryptonote_core/tx_sanity_check.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h" #include "wallet2.h"
#include "wallet_args.h" #include "wallet_args.h"
#include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/cryptonote_format_utils.h"
#include "net/parse.h" #include "net/parse.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "rpc/core_rpc_server_error_codes.h" #include "rpc/core_rpc_server_error_codes.h"
#include "rpc/rpc_payment_signature.h"
#include "rpc/rpc_payment_costs.h"
#include "misc_language.h" #include "misc_language.h"
#include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/cryptonote_basic_impl.h"
#include "multisig/multisig.h" #include "multisig/multisig.h"
@ -1191,15 +1188,13 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_show_wallet_name_when_locked(false), m_show_wallet_name_when_locked(false),
m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT), m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT),
m_setup_background_mining(BackgroundMiningMaybe), m_setup_background_mining(BackgroundMiningMaybe),
m_persistent_rpc_client_id(false),
m_auto_mine_for_rpc_payment_threshold(-1.0f),
m_is_initialized(false), m_is_initialized(false),
m_kdf_rounds(kdf_rounds), m_kdf_rounds(kdf_rounds),
is_old_file_format(false), is_old_file_format(false),
m_watch_only(false), m_watch_only(false),
m_multisig(false), m_multisig(false),
m_multisig_threshold(0), m_multisig_threshold(0),
m_node_rpc_proxy(*m_http_client, m_rpc_payment_state, m_daemon_rpc_mutex), m_node_rpc_proxy(*m_http_client, m_daemon_rpc_mutex),
m_account_public_address{crypto::null_pkey, crypto::null_pkey}, m_account_public_address{crypto::null_pkey, crypto::null_pkey},
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
@ -1223,13 +1218,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_rpc_version(0), m_rpc_version(0),
m_export_format(ExportFormat::Binary), m_export_format(ExportFormat::Binary),
m_load_deprecated_formats(false), m_load_deprecated_formats(false),
m_credits_target(0),
m_enable_multisig(false), m_enable_multisig(false),
m_pool_info_query_time(0), m_pool_info_query_time(0),
m_has_ever_refreshed_from_node(false), m_has_ever_refreshed_from_node(false),
m_allow_mismatched_daemon_version(false) m_allow_mismatched_daemon_version(false)
{ {
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
} }
wallet2::~wallet2() wallet2::~wallet2()
@ -1343,11 +1336,6 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
m_trusted_daemon = trusted_daemon; m_trusted_daemon = trusted_daemon;
if (changed) if (changed)
{ {
if (!m_persistent_rpc_client_id) {
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
}
m_rpc_payment_state.expected_spent = 0;
m_rpc_payment_state.discrepancy = 0;
m_rpc_version = 0; m_rpc_version = 0;
m_node_rpc_proxy.invalidate(); m_node_rpc_proxy.invalidate();
m_pool_info_query_time = 0; m_pool_info_query_time = 0;
@ -1647,7 +1635,6 @@ void wallet2::scan_tx(const std::vector<crypto::hash> &txids)
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to get transaction from daemon"); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to get transaction from daemon");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error, "Failed to get transaction from daemon"); THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error, "Failed to get transaction from daemon");
@ -2760,15 +2747,11 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status));
THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
"mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" + "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon"); boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH;
check_rpc_cost("/getblocks.bin", res.credits, pre_call_credits, 1 + res.blocks.size() * COST_PER_BLOCK + pool_info_cost);
} }
blocks_start_height = res.start_height; blocks_start_height = res.start_height;
@ -2809,11 +2792,8 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(res.status));
check_rpc_cost("/gethashes.bin", res.credits, pre_call_credits, 1 + res.m_block_ids.size() * COST_PER_BLOCK_HASH);
} }
blocks_start_height = res.start_height; blocks_start_height = res.start_height;
@ -3308,12 +3288,8 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status));
uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH;
check_rpc_cost("/getblocks.bin", res.credits, pre_call_credits, pool_info_cost);
} }
m_pool_info_query_time = res.daemon_time; m_pool_info_query_time = res.daemon_time;
@ -3349,11 +3325,8 @@ void wallet2::update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote:
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_transaction_pool_hashes.bin", error::get_tx_pool_error); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_transaction_pool_hashes.bin", error::get_tx_pool_error);
check_rpc_cost("/get_transaction_pool_hashes.bin", res.credits, pre_call_credits, 1 + res.tx_hashes.size() * COST_PER_POOL_HASH);
} }
MTRACE("update_pool_state_by_pool_query got pool"); MTRACE("update_pool_state_by_pool_query got pool");
@ -3813,9 +3786,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
throw; throw;
} }
catch (const error::payment_required&) catch (const error::deprecated_rpc_access&)
{ {
// no point in trying again, it'd just eat up credits
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
throw; throw;
} }
@ -3900,11 +3872,8 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
try try
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, *m_http_client, rpc_timeout); r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_output_distribution.bin"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_output_distribution.bin");
check_rpc_cost("/get_output_distribution.bin", res.credits, pre_call_credits, COST_PER_OUTPUT_DISTRIBUTION_0);
} }
catch(...) catch(...)
{ {
@ -4283,13 +4252,16 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
json.AddMember("original_view_secret_key", value, json.GetAllocator()); json.AddMember("original_view_secret_key", value, json.GetAllocator());
} }
value2.SetInt(m_persistent_rpc_client_id ? 1 : 0); // This value is serialized for compatibility with wallets which support the pay-to-use RPC system
value2.SetInt(0);
json.AddMember("persistent_rpc_client_id", value2, json.GetAllocator()); json.AddMember("persistent_rpc_client_id", value2, json.GetAllocator());
value2.SetFloat(m_auto_mine_for_rpc_payment_threshold); // This value is serialized for compatibility with wallets which support the pay-to-use RPC system
value2.SetFloat(0.0f);
json.AddMember("auto_mine_for_rpc_payment", value2, json.GetAllocator()); json.AddMember("auto_mine_for_rpc_payment", value2, json.GetAllocator());
value2.SetUint64(m_credits_target); // This value is serialized for compatibility with wallets which support the pay-to-use RPC system
value2.SetUint64(0);
json.AddMember("credits_target", value2, json.GetAllocator()); json.AddMember("credits_target", value2, json.GetAllocator());
value2.SetInt(m_enable_multisig ? 1 : 0); value2.SetInt(m_enable_multisig ? 1 : 0);
@ -4440,9 +4412,6 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_device_derivation_path = ""; m_device_derivation_path = "";
m_key_device_type = hw::device::device_type::SOFTWARE; m_key_device_type = hw::device::device_type::SOFTWARE;
encrypted_secret_keys = false; encrypted_secret_keys = false;
m_persistent_rpc_client_id = false;
m_auto_mine_for_rpc_payment_threshold = -1.0f;
m_credits_target = 0;
m_enable_multisig = false; m_enable_multisig = false;
m_allow_mismatched_daemon_version = false; m_allow_mismatched_daemon_version = false;
} }
@ -4670,13 +4639,6 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_original_keys_available = false; m_original_keys_available = false;
} }
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, persistent_rpc_client_id, int, Int, false, false);
m_persistent_rpc_client_id = field_persistent_rpc_client_id;
// save as float, load as double, because it can happen you can't load back as float...
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_mine_for_rpc_payment, float, Double, false, FLT_MAX);
m_auto_mine_for_rpc_payment_threshold = field_auto_mine_for_rpc_payment;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, credits_target, uint64_t, Uint64, false, 0);
m_credits_target = field_credits_target;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false);
m_enable_multisig = field_enable_multisig; m_enable_multisig = field_enable_multisig;
} }
@ -5905,9 +5867,6 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
} }
if (!m_persistent_rpc_client_id)
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
cryptonote::block genesis; cryptonote::block genesis;
generate_genesis(genesis); generate_genesis(genesis);
crypto::hash genesis_hash = get_block_hash(genesis); crypto::hash genesis_hash = get_block_hash(genesis);
@ -5965,11 +5924,7 @@ void wallet2::trim_hashchain()
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req.height = m_blockchain.size() - 1; req.height = m_blockchain.size() - 1;
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, *m_http_client, rpc_timeout); r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, *m_http_client, rpc_timeout);
if (r && res.status == CORE_RPC_STATUS_OK)
check_rpc_cost("getblockheaderbyheight", res.credits, pre_call_credits, COST_PER_BLOCK_HEADER);
} }
if (r && res.status == CORE_RPC_STATUS_OK) if (r && res.status == CORE_RPC_STATUS_OK)
@ -6381,14 +6336,11 @@ void wallet2::rescan_spent()
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(daemon_resp.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(daemon_resp.status));
THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
"daemon returned wrong response for is_key_image_spent, wrong amounts count = " + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs)); std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs));
check_rpc_cost("/is_key_image_spent", daemon_resp.credits, pre_call_credits, n_outputs * COST_PER_KEY_IMAGE);
} }
std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status)); std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
@ -6731,11 +6683,8 @@ void wallet2::commit_tx(pending_tx& ptx)
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
check_rpc_cost("/sendrawtransaction", daemon_send_resp.credits, pre_call_credits, COST_PER_TX_RELAY);
} }
// sanity checks // sanity checks
@ -7859,11 +7808,8 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
getbh_req.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(getbh_res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(getbh_res.status));
check_rpc_cost("/getblockheadersrange", getbh_res.credits, pre_call_credits, N * COST_PER_BLOCK_HEADER);
} }
if (getbh_res.headers.size() != N) if (getbh_res.headers.size() != N)
@ -8102,14 +8048,11 @@ bool wallet2::find_and_save_rings(bool force)
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size())); std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
} }
MDEBUG("Scanning " << res.txs.size() << " transactions"); MDEBUG("Scanning " << res.txs.size() << " transactions");
@ -8447,11 +8390,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(resp_t.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(resp_t.status));
check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size());
} }
} }
@ -8474,13 +8414,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, *m_http_client, rpc_timeout * 1000); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, *m_http_client, rpc_timeout * 1000);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(resp_t.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(resp_t.status));
uint64_t expected_cost = 0;
for (uint64_t amount: req_t.amounts) expected_cost += (amount ? COST_PER_OUTPUT_DISTRIBUTION : COST_PER_OUTPUT_DISTRIBUTION_0);
check_rpc_cost("get_output_distribution", resp_t.credits, pre_call_credits, expected_cost);
} }
// check we got all data // check we got all data
@ -8862,14 +8797,11 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
chunk_req.outputs.push_back(req.outputs[offset + i]); chunk_req.outputs.push_back(req.outputs[offset + i]);
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
chunk_req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", chunk_req, chunk_daemon_resp, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", chunk_req, chunk_daemon_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, chunk_daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(chunk_daemon_resp.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, chunk_daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(chunk_daemon_resp.status));
THROW_WALLET_EXCEPTION_IF(chunk_daemon_resp.outs.size() != chunk_req.outputs.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(chunk_daemon_resp.outs.size() != chunk_req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " + "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(chunk_daemon_resp.outs.size()) + ", expected " + std::to_string(chunk_req.outputs.size())); std::to_string(chunk_daemon_resp.outs.size()) + ", expected " + std::to_string(chunk_req.outputs.size()));
check_rpc_cost("/get_outs.bin", chunk_daemon_resp.credits, pre_call_credits, chunk_daemon_resp.outs.size() * COST_PER_OUT);
offset += chunk_size; offset += chunk_size;
for (size_t i = 0; i < chunk_daemon_resp.outs.size(); ++i) for (size_t i = 0; i < chunk_daemon_resp.outs.size(); ++i)
@ -11302,12 +11234,8 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status);
uint64_t cost = req_t.amounts.empty() ? COST_PER_FULL_OUTPUT_HISTOGRAM : (COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size());
check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, cost);
} }
std::set<uint64_t> mixable; std::set<uint64_t> mixable;
@ -11344,13 +11272,10 @@ uint64_t wallet2::get_num_rct_outputs()
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status);
THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response"); THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount"); THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, COST_PER_OUTPUT_HISTOGRAM);
} }
return resp_t.histogram[0].total_instances; return resp_t.histogram[0].total_instances;
@ -11475,12 +11400,9 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client); bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -11525,17 +11447,13 @@ void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_
req.prune = true; req.prune = true;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r; bool r;
uint64_t pre_call_credits;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
std::to_string(res.txs.size()) + ", expected 1"); std::to_string(res.txs.size()) + ", expected 1");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -11588,17 +11506,13 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
req.prune = true; req.prune = true;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r; bool r;
uint64_t pre_call_credits;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
std::to_string(res.txs.size()) + ", expected 1"); std::to_string(res.txs.size()) + ", expected 1");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -11651,17 +11565,13 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
} }
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
bool r; bool r;
uint64_t pre_call_credits;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, *m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status);
THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " + "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size)); std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
check_rpc_cost("/get_outs.bin", res.credits, pre_call_credits, ring_size * COST_PER_OUT);
} }
// copy pubkey pointers // copy pubkey pointers
@ -11709,17 +11619,13 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
req.prune = true; req.prune = true;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r; bool r;
uint64_t pre_call_credits;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
std::to_string(res.txs.size()) + ", expected 1"); std::to_string(res.txs.size()) + ", expected 1");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -11784,17 +11690,13 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
} }
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
bool r; bool r;
uint64_t pre_call_credits;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, *m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status);
THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " + "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size())); std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
check_rpc_cost("/get_outs.bin", res.credits, pre_call_credits, req.outputs.size() * COST_PER_OUT);
} }
// copy pointers // copy pointers
@ -11880,12 +11782,9 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
bool ok; bool ok;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client); ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -11978,12 +11877,9 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
bool ok; bool ok;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client); ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -12139,12 +12035,9 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
bool ok; bool ok;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client); ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
} }
cryptonote::transaction tx; cryptonote::transaction tx;
@ -12462,12 +12355,9 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
gettx_req.client = get_client_signature();
bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, *m_http_client); bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(), THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
error::wallet_internal_error, "Failed to get transaction from daemon"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", gettx_res.credits, pre_call_credits, gettx_res.txs.size() * COST_PER_TX);
} }
// check spent status // check spent status
@ -12479,12 +12369,9 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
bool ok; bool ok;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
kispent_req.client = get_client_signature();
ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, *m_http_client, rpc_timeout); ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, *m_http_client, rpc_timeout);
THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(), THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
error::wallet_internal_error, "Failed to get key image spent status from daemon"); error::wallet_internal_error, "Failed to get key image spent status from daemon");
check_rpc_cost("/is_key_image_spent", kispent_res.credits, pre_call_credits, kispent_res.spent_status.size() * COST_PER_KEY_IMAGE);
} }
total = spent = 0; total = spent = 0;
@ -13134,14 +13021,11 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
PERF_TIMER(import_key_images_RPC); PERF_TIMER(import_key_images_RPC);
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, daemon_resp, "is_key_image_spent"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, daemon_resp, "is_key_image_spent");
THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
"daemon returned wrong response for is_key_image_spent, wrong amounts count = " + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size())); std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
check_rpc_cost("/is_key_image_spent", daemon_resp.credits, pre_call_credits, daemon_resp.spent_status.size() * COST_PER_KEY_IMAGE);
} }
for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n) for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
@ -13223,13 +13107,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
PERF_TIMER_START(import_key_images_E); PERF_TIMER_START(import_key_images_E);
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
gettxs_req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, *m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, gettxs_res, "gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, gettxs_res, "gettransactions");
THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size())); "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
check_rpc_cost("/gettransactions", gettxs_res.credits, pre_call_credits, spent_txids.size() * COST_PER_TX);
} }
PERF_TIMER_STOP(import_key_images_E); PERF_TIMER_STOP(import_key_images_E);
@ -14379,11 +14260,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
bool r; bool r;
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, *m_http_client, rpc_timeout); r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, *m_http_client, rpc_timeout);
if (r && res.status == CORE_RPC_STATUS_OK)
check_rpc_cost("/getblocks_by_height.bin", res.credits, pre_call_credits, 3 * COST_PER_BLOCK);
} }
if (!r || res.status != CORE_RPC_STATUS_OK) if (!r || res.status != CORE_RPC_STATUS_OK)
@ -14457,11 +14334,8 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, *m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_txpool_backlog", error::get_tx_pool_error); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_txpool_backlog", error::get_tx_pool_error);
check_rpc_cost("get_txpool_backlog", res.credits, pre_call_credits, COST_PER_TX_POOL_STATS * res.backlog.size());
} }
uint64_t block_weight_limit = 0; uint64_t block_weight_limit = 0;
@ -14612,13 +14486,17 @@ std::string wallet2::get_rpc_status(const std::string &s) const
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const void wallet2::throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const
{ {
// Treat all RPC payment access errors the same, whether payment is actually required or not
THROW_WALLET_EXCEPTION_IF(error.code == CORE_RPC_ERROR_CODE_INVALID_CLIENT, tools::error::deprecated_rpc_access, method);
THROW_WALLET_EXCEPTION_IF(error.code, tools::error::wallet_coded_rpc_error, method, error.code, get_rpc_server_error_message(error.code)); THROW_WALLET_EXCEPTION_IF(error.code, tools::error::wallet_coded_rpc_error, method, error.code, get_rpc_server_error_message(error.code));
THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, method); THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, method);
// empty string -> not connection // empty string -> not connection
THROW_WALLET_EXCEPTION_IF(status.empty(), tools::error::no_connection_to_daemon, method); THROW_WALLET_EXCEPTION_IF(status.empty(), tools::error::no_connection_to_daemon, method);
THROW_WALLET_EXCEPTION_IF(status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method); THROW_WALLET_EXCEPTION_IF(status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method);
THROW_WALLET_EXCEPTION_IF(status == CORE_RPC_STATUS_PAYMENT_REQUIRED, tools::error::payment_required, method); THROW_WALLET_EXCEPTION_IF(status == CORE_RPC_STATUS_PAYMENT_REQUIRED, tools::error::deprecated_rpc_access, method);
// Deprecated RPC payment access endpoints would set status to "Client signature does not verify for <method>"
THROW_WALLET_EXCEPTION_IF(status.compare(0, 16, "Client signature") == 0, tools::error::deprecated_rpc_access, method);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------

View File

@ -71,14 +71,12 @@
#include "node_rpc_proxy.h" #include "node_rpc_proxy.h"
#include "message_store.h" #include "message_store.h"
#include "wallet_light_rpc.h" #include "wallet_light_rpc.h"
#include "wallet_rpc_helpers.h"
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
#define THROW_ON_RPC_RESPONSE_ERROR(r, error, res, method, ...) \ #define THROW_ON_RPC_RESPONSE_ERROR(r, error, res, method, ...) \
do { \ do { \
handle_payment_changes(res, std::integral_constant<bool, HasCredits<decltype(res)>::Has>()); \
throw_on_rpc_response_error(r, error, res.status, method); \ throw_on_rpc_response_error(r, error, res.status, method); \
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, ## __VA_ARGS__); \ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, ## __VA_ARGS__); \
} while(0) } while(0)
@ -1234,7 +1232,8 @@ private:
a & m_cold_key_images.parent(); a & m_cold_key_images.parent();
if(ver < 29) if(ver < 29)
return; return;
a & m_rpc_client_secret_key; crypto::secret_key dummy_rpc_client_secret_key; // Compatibility for old RPC payment system
a & dummy_rpc_client_secret_key;
if(ver < 30) if(ver < 30)
{ {
m_has_ever_refreshed_from_node = false; m_has_ever_refreshed_from_node = false;
@ -1270,7 +1269,8 @@ private:
FIELD(m_tx_device) FIELD(m_tx_device)
FIELD(m_device_last_key_image_sync) FIELD(m_device_last_key_image_sync)
FIELD(m_cold_key_images) FIELD(m_cold_key_images)
FIELD(m_rpc_client_secret_key) crypto::secret_key dummy_rpc_client_secret_key; // Compatibility for old RPC payment system
FIELD_N("m_rpc_client_secret_key", dummy_rpc_client_secret_key)
if (version < 1) if (version < 1)
{ {
m_has_ever_refreshed_from_node = false; m_has_ever_refreshed_from_node = false;
@ -1354,14 +1354,6 @@ private:
inline void set_export_format(const ExportFormat& export_format) { m_export_format = export_format; } inline void set_export_format(const ExportFormat& export_format) { m_export_format = export_format; }
bool load_deprecated_formats() const { return m_load_deprecated_formats; } bool load_deprecated_formats() const { return m_load_deprecated_formats; }
void load_deprecated_formats(bool load) { m_load_deprecated_formats = load; } void load_deprecated_formats(bool load) { m_load_deprecated_formats = load; }
bool persistent_rpc_client_id() const { return m_persistent_rpc_client_id; }
void persistent_rpc_client_id(bool persistent) { m_persistent_rpc_client_id = persistent; }
void auto_mine_for_rpc_payment_threshold(float threshold) { m_auto_mine_for_rpc_payment_threshold = threshold; }
float auto_mine_for_rpc_payment_threshold() const { return m_auto_mine_for_rpc_payment_threshold; }
crypto::secret_key get_rpc_client_secret_key() const { return m_rpc_client_secret_key; }
void set_rpc_client_secret_key(const crypto::secret_key &key) { m_rpc_client_secret_key = key; m_node_rpc_proxy.set_client_secret_key(key); }
uint64_t credits_target() const { return m_credits_target; }
void credits_target(uint64_t threshold) { m_credits_target = threshold; }
bool is_multisig_enabled() const { return m_enable_multisig; } bool is_multisig_enabled() const { return m_enable_multisig; }
void enable_multisig(bool enable) { m_enable_multisig = enable; } void enable_multisig(bool enable) { m_enable_multisig = enable; }
bool is_mismatched_daemon_version_allowed() const { return m_allow_mismatched_daemon_version; } bool is_mismatched_daemon_version_allowed() const { return m_allow_mismatched_daemon_version; }
@ -1545,21 +1537,6 @@ private:
std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size);
bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
bool daemon_requires_payment();
bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance);
bool search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc = NULL, const std::function<void(const std::string&)> &errorfunc = NULL);
template<typename T> void handle_payment_changes(const T &res, std::true_type) {
if (res.status == CORE_RPC_STATUS_OK || res.status == CORE_RPC_STATUS_PAYMENT_REQUIRED)
m_rpc_payment_state.credits = res.credits;
if (res.top_hash != m_rpc_payment_state.top_hash)
{
m_rpc_payment_state.top_hash = res.top_hash;
m_rpc_payment_state.stale = true;
}
}
template<typename T> void handle_payment_changes(const T &res, std::false_type) {}
// Light wallet specific functions // Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers // fetch unspent outs from lw node and store in m_transfers
void light_wallet_get_unspent_outs(); void light_wallet_get_unspent_outs();
@ -1674,9 +1651,6 @@ private:
void set_offline(bool offline = true); void set_offline(bool offline = true);
bool is_offline() const { return m_offline; } bool is_offline() const { return m_offline; }
uint64_t credits() const { return m_rpc_payment_state.credits; }
void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; }
static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
private: private:
@ -1785,9 +1759,6 @@ private:
std::string get_rpc_status(const std::string &s) const; std::string get_rpc_status(const std::string &s) const;
void throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const; void throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const;
std::string get_client_signature() const;
void check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_credits, double expected_cost);
bool should_expand(const cryptonote::subaddress_index &index) const; bool should_expand(const cryptonote::subaddress_index &index) const;
bool spends_one_of_ours(const cryptonote::transaction &tx) const; bool spends_one_of_ours(const cryptonote::transaction &tx) const;
@ -1874,7 +1845,6 @@ private:
bool m_show_wallet_name_when_locked; bool m_show_wallet_name_when_locked;
uint32_t m_inactivity_lock_timeout; uint32_t m_inactivity_lock_timeout;
BackgroundMiningSetupType m_setup_background_mining; BackgroundMiningSetupType m_setup_background_mining;
bool m_persistent_rpc_client_id;
float m_auto_mine_for_rpc_payment_threshold; float m_auto_mine_for_rpc_payment_threshold;
bool m_is_initialized; bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy; NodeRPCProxy m_node_rpc_proxy;
@ -1886,9 +1856,6 @@ private:
bool m_use_dns; bool m_use_dns;
bool m_offline; bool m_offline;
uint32_t m_rpc_version; uint32_t m_rpc_version;
crypto::secret_key m_rpc_client_secret_key;
rpc_payment_state_t m_rpc_payment_state;
uint64_t m_credits_target;
bool m_enable_multisig; bool m_enable_multisig;
bool m_allow_mismatched_daemon_version; bool m_allow_mismatched_daemon_version;

View File

@ -76,10 +76,6 @@ namespace wallet_args
{ {
return {"wallet-file", wallet_args::tr("Use wallet <arg>"), ""}; return {"wallet-file", wallet_args::tr("Use wallet <arg>"), ""};
} }
command_line::arg_descriptor<std::string> arg_rpc_client_secret_key()
{
return {"rpc-client-secret-key", wallet_args::tr("Set RPC client secret key for RPC payments"), ""};
}
command_line::arg_descriptor<std::string> arg_password_file() command_line::arg_descriptor<std::string> arg_password_file()
{ {
return {"password-file", wallet_args::tr("Wallet password file"), ""}; return {"password-file", wallet_args::tr("Wallet password file"), ""};

View File

@ -36,7 +36,6 @@ namespace wallet_args
{ {
command_line::arg_descriptor<std::string> arg_generate_from_json(); command_line::arg_descriptor<std::string> arg_generate_from_json();
command_line::arg_descriptor<std::string> arg_wallet_file(); command_line::arg_descriptor<std::string> arg_wallet_file();
command_line::arg_descriptor<std::string> arg_rpc_client_secret_key();
command_line::arg_descriptor<std::string> arg_password_file(); command_line::arg_descriptor<std::string> arg_password_file();
const char* tr(const char* str); const char* tr(const char* str);

View File

@ -91,7 +91,7 @@ namespace tools
// is_key_image_spent_error // is_key_image_spent_error
// get_histogram_error // get_histogram_error
// get_output_distribution // get_output_distribution
// payment_required // deprecated_rpc_access
// wallet_files_doesnt_correspond // wallet_files_doesnt_correspond
// //
// * - class with protected ctor // * - class with protected ctor
@ -865,10 +865,11 @@ namespace tools
} }
}; };
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct payment_required: public wallet_rpc_error struct deprecated_rpc_access: public wallet_rpc_error
{ {
explicit payment_required(std::string&& loc, const std::string& request) // The daemon we connected to has enabled the old pay-to-access RPC feature
: wallet_rpc_error(std::move(loc), "payment required", request) explicit deprecated_rpc_access(std::string&& loc, const std::string& request)
: wallet_rpc_error(std::move(loc), "daemon requires deprecated RPC payment", request)
{ {
} }
}; };

View File

@ -1,95 +0,0 @@
// Copyright (c) 2018-2023, 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.
#pragma once
#include <limits>
#include <type_traits>
namespace
{
// credits to yrp (https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature
template <typename T>
struct HasCredits
{
template<typename U, uint64_t (U::*)> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::credits>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};
}
namespace tools
{
struct rpc_payment_state_t
{
uint64_t credits;
uint64_t expected_spent;
uint64_t discrepancy;
std::string top_hash;
bool stale;
rpc_payment_state_t(): credits(0), expected_spent(0), discrepancy(0), stale(true) {}
};
static inline void check_rpc_cost(rpc_payment_state_t &rpc_payment_state, const char *call, uint64_t post_call_credits, uint64_t pre_call_credits, double expected_cost)
{
uint64_t expected_credits = (uint64_t)expected_cost;
if (expected_credits == 0)
expected_credits = 1;
rpc_payment_state.credits = post_call_credits;
rpc_payment_state.expected_spent += expected_credits;
if (pre_call_credits <= post_call_credits)
return;
uint64_t cost = pre_call_credits - post_call_credits;
if (cost == expected_credits)
{
MDEBUG("Call " << call << " cost " << cost << " credits");
return;
}
MWARNING("Call " << call << " cost " << cost << " credits, expected " << expected_credits);
if (cost > expected_credits)
{
uint64_t d = cost - expected_credits;
if (rpc_payment_state.discrepancy > std::numeric_limits<uint64_t>::max() - d)
{
MERROR("Integer overflow in credit discrepancy calculation, setting to max");
rpc_payment_state.discrepancy = std::numeric_limits<uint64_t>::max();
}
else
{
rpc_payment_state.discrepancy += d;
}
}
}
}

View File

@ -1,212 +0,0 @@
// Copyright (c) 2018-2023, 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/utility/value_init.hpp>
#include "include_base_utils.h"
#include "cryptonote_config.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "rpc/rpc_payment_signature.h"
#include "misc_language.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "int-util.h"
#include "crypto/crypto.h"
#include "cryptonote_basic/blobdatatype.h"
#include "common/i18n.h"
#include "common/util.h"
#include "common/threadpool.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2.rpc_payments"
#define RPC_PAYMENT_POLL_PERIOD 10 /* seconds*/
namespace tools
{
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_client_signature() const
{
return cryptonote::make_rpc_payment_signature(m_rpc_client_secret_key);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie)
{
boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_payment_info(mining, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie);
credits = m_rpc_payment_state.credits;
if (result && *result != CORE_RPC_STATUS_OK)
return false;
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::daemon_requires_payment()
{
bool payment_required = false;
uint64_t credits, diff, credits_per_hash_found, height, seed_height;
uint32_t cookie;
cryptonote::blobdata blob;
crypto::hash seed_hash, next_seed_hash;
return get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance)
{
cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::response res = AUTO_VAL_INIT(res);
req.nonce = nonce;
req.cookie = cookie;
m_daemon_rpc_mutex.lock();
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
epee::json_rpc::error error;
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "rpc_access_submit_nonce", req, res, error, *m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, error, res, "rpc_access_submit_nonce");
THROW_WALLET_EXCEPTION_IF(res.credits < pre_call_credits, error::wallet_internal_error, "RPC payment did not increase balance");
if (m_rpc_payment_state.top_hash != res.top_hash)
{
m_rpc_payment_state.top_hash = res.top_hash;
m_rpc_payment_state.stale = true;
}
m_rpc_payment_state.credits = res.credits;
balance = res.credits;
credits = balance - pre_call_credits;
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc, const std::function<void(const std::string&)> &errorfunc)
{
bool need_payment = false;
bool payment_required;
uint64_t credits, diff, credits_per_hash_found, height, seed_height;
uint32_t cookie;
unsigned int n_hashes = 0;
cryptonote::blobdata hashing_blob;
crypto::hash seed_hash, next_seed_hash;
try
{
need_payment = get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required && credits < credits_target;
if (!need_payment)
return true;
if (!startfunc(diff, credits_per_hash_found))
return true;
}
catch (const std::exception &e) { return false; }
static std::atomic<uint32_t> nonce(0);
while (contfunc(n_hashes))
{
try
{
need_payment = get_rpc_payment_info(true, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required && credits < credits_target;
if (!need_payment)
return true;
}
catch (const std::exception &e) { return false; }
if (hashing_blob.empty())
{
MERROR("Bad hashing blob from daemon");
if (errorfunc)
errorfunc("Bad hashing blob from daemon, trying again");
epee::misc_utils::sleep_no_w(1000);
continue;
}
if(n_threads == 0)
n_threads = boost::thread::hardware_concurrency();
std::vector<crypto::hash> hash(n_threads);
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
tools::threadpool::waiter waiter(tpool);
const uint32_t local_nonce = nonce += n_threads; // wrapping's OK
for (size_t i = 0; i < n_threads; i++)
{
tpool.submit(&waiter, [&, i] {
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
const uint8_t major_version = hashing_blob[0];
if (major_version >= RX_BLOCK_VERSION)
{
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
}
else
{
int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash[i], cn_variant, height);
}
});
}
waiter.wait();
n_hashes += n_threads;
for(size_t i=0; i < n_threads; i++)
{
if (cryptonote::check_hash(hash[i], diff))
{
uint64_t credits, balance;
try
{
make_rpc_payment(local_nonce-i, cookie, credits, balance);
if (credits != credits_per_hash_found)
{
MERROR("Found nonce, but daemon did not credit us with the expected amount");
if (errorfunc)
errorfunc("Found nonce, but daemon did not credit us with the expected amount");
return false;
}
MDEBUG("Found nonce " << local_nonce-i << " at diff " << diff << ", gets us " << credits_per_hash_found << ", now " << balance << " credits");
if (!foundfunc(credits))
break;
}
catch (const tools::error::wallet_coded_rpc_error &e)
{
MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
if (errorfunc)
errorfunc("Found nonce, but daemon errored out with error " + std::to_string(e.code()) + ": " + e.status() + ", continuing");
}
catch (const std::exception &e)
{
MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
if (errorfunc)
errorfunc("Found nonce, but daemon errored out with: '" + std::string(e.what()) + "', continuing");
}
}
}
}
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_call_credits, double expected_cost)
{
return tools::check_rpc_cost(m_rpc_payment_state, call, post_call_credits, pre_call_credits, expected_cost);
}
//----------------------------------------------------------------------------------------------------
}

View File

@ -4517,7 +4517,6 @@ public:
const auto arg_wallet_file = wallet_args::arg_wallet_file(); const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json(); const auto arg_from_json = wallet_args::arg_generate_from_json();
const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
const auto arg_password_file = wallet_args::arg_password_file(); const auto arg_password_file = wallet_args::arg_password_file();
const auto wallet_file = command_line::get_arg(vm, arg_wallet_file); const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
@ -4575,17 +4574,6 @@ public:
return false; return false;
} }
if (!command_line::is_arg_defaulted(vm, arg_rpc_client_secret_key))
{
crypto::secret_key client_secret_key;
if (!epee::string_tools::hex_to_pod(command_line::get_arg(vm, arg_rpc_client_secret_key), client_secret_key))
{
MERROR(arg_rpc_client_secret_key.name << ": RPC client secret key should be 32 byte in hex format");
return false;
}
wal->set_rpc_client_secret_key(client_secret_key);
}
bool quit = false; bool quit = false;
tools::signal_handler::install([&wal, &quit](int) { tools::signal_handler::install([&wal, &quit](int) {
assert(wal); assert(wal);
@ -4692,7 +4680,6 @@ int main(int argc, char** argv) {
const auto arg_wallet_file = wallet_args::arg_wallet_file(); const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json(); const auto arg_from_json = wallet_args::arg_generate_from_json();
const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
po::options_description hidden_options("Hidden"); po::options_description hidden_options("Hidden");
@ -4706,7 +4693,6 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_from_json); command_line::add_arg(desc_params, arg_from_json);
command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password); command_line::add_arg(desc_params, arg_prompt_for_password);
command_line::add_arg(desc_params, arg_rpc_client_secret_key);
command_line::add_arg(desc_params, arg_no_initial_sync); command_line::add_arg(desc_params, arg_no_initial_sync);
daemonizer::init_options(hidden_options, desc_params); daemonizer::init_options(hidden_options, desc_params);