aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/core_rpc_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/core_rpc_server.cpp')
-rw-r--r--src/rpc/core_rpc_server.cpp773
1 files changed, 656 insertions, 117 deletions
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index dfc4d4cf3..8882df5d7 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -39,6 +39,7 @@ using namespace epee;
#include "common/download.h"
#include "common/util.h"
#include "common/perf_timer.h"
+#include "int-util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
@@ -49,6 +50,8 @@ using namespace epee;
#include "crypto/hash.h"
#include "rpc/rpc_args.h"
#include "rpc/rpc_handler.h"
+#include "rpc/rpc_payment_costs.h"
+#include "rpc/rpc_payment_signature.h"
#include "core_rpc_server_error_codes.h"
#include "p2p/net_node.h"
#include "version.h"
@@ -61,8 +64,50 @@ using namespace epee;
#define OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION (3 * 86400) // 3 days max, the wallet requests 1.8 days
+#define DEFAULT_PAYMENT_DIFFICULTY 1000
+#define DEFAULT_PAYMENT_CREDITS_PER_HASH 100
+
+#define RPC_TRACKER(rpc) \
+ PERF_TIMER(rpc); \
+ RPCTracker tracker(#rpc, PERF_TIMER_NAME(rpc))
+
namespace
{
+ class RPCTracker
+ {
+ public:
+ struct entry_t
+ {
+ uint64_t count;
+ uint64_t time;
+ uint64_t credits;
+ };
+
+ RPCTracker(const char *rpc, tools::LoggingPerformanceTimer &timer): rpc(rpc), timer(timer) {
+ }
+ ~RPCTracker() {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ auto &e = tracker[rpc];
+ ++e.count;
+ e.time += timer.value();
+ }
+ void pay(uint64_t amount) {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ auto &e = tracker[rpc];
+ e.credits += amount;
+ }
+ const std::string &rpc_name() const { return rpc; }
+ static void clear() { boost::unique_lock<boost::mutex> lock(mutex); tracker.clear(); }
+ static std::unordered_map<std::string, entry_t> data() { boost::unique_lock<boost::mutex> lock(mutex); return tracker; }
+ private:
+ std::string rpc;
+ tools::LoggingPerformanceTimer &timer;
+ static boost::mutex mutex;
+ static std::unordered_map<std::string, entry_t> tracker;
+ };
+ boost::mutex RPCTracker::mutex;
+ std::unordered_map<std::string, RPCTracker::entry_t> RPCTracker::tracker;
+
void add_reason(std::string &reasons, const char *reason)
{
if (!reasons.empty())
@@ -95,6 +140,9 @@ namespace cryptonote
command_line::add_arg(desc, arg_bootstrap_daemon_address);
command_line::add_arg(desc, arg_bootstrap_daemon_login);
cryptonote::rpc_args::init_options(desc, true);
+ command_line::add_arg(desc, arg_rpc_payment_address);
+ command_line::add_arg(desc, arg_rpc_payment_difficulty);
+ command_line::add_arg(desc, arg_rpc_payment_credits);
}
//------------------------------------------------------------------------------------------------------------------------------
core_rpc_server::core_rpc_server(
@@ -173,6 +221,11 @@ namespace cryptonote
return true;
}
+ core_rpc_server::~core_rpc_server()
+ {
+ if (m_rpc_payment)
+ m_rpc_payment->store();
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
@@ -188,6 +241,45 @@ namespace cryptonote
if (!rpc_config)
return false;
+ std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
+ if (!address.empty())
+ {
+ if (!m_restricted && nettype() != FAKECHAIN)
+ {
+ MERROR("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment");
+ return false;
+ }
+ cryptonote::address_parse_info info;
+ if (!get_account_address_from_str(info, nettype(), address))
+ {
+ MERROR("Invalid payment address: " << address);
+ return false;
+ }
+ if (info.is_subaddress)
+ {
+ MERROR("Payment address may not be a subaddress: " << address);
+ return false;
+ }
+ uint64_t diff = command_line::get_arg(vm, arg_rpc_payment_difficulty);
+ uint64_t credits = command_line::get_arg(vm, arg_rpc_payment_credits);
+ if (diff == 0 || credits == 0)
+ {
+ MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given");
+ return false;
+ }
+ m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
+ m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir));
+ m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff));
+ }
+
+ if (!m_rpc_payment)
+ {
+ uint32_t bind_ip;
+ bool ok = epee::string_tools::get_ip_int32_from_string(bind_ip, rpc_config->bind_ip);
+ if (ok & !epee::net_utils::is_ip_loopback(bind_ip))
+ MWARNING("The RPC server is accessible from the outside, but no RPC payment was setup. RPC access will be free for all.");
+ }
+
if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
command_line::get_arg(vm, arg_bootstrap_daemon_login)))
{
@@ -200,6 +292,9 @@ namespace cryptonote
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
+ if (m_rpc_payment)
+ m_net_server.add_idle_handler([this](){ return m_rpc_payment->on_idle(); }, 60 * 1000);
+
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
rng, std::move(port), std::move(rpc_config->bind_ip),
@@ -208,6 +303,45 @@ namespace cryptonote
);
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::check_payment(const std::string &client_message, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash)
+ {
+ if (m_rpc_payment == NULL)
+ {
+ credits = 0;
+ return true;
+ }
+ uint64_t height;
+ crypto::hash hash;
+ m_core.get_blockchain_top(height, hash);
+ top_hash = epee::string_tools::pod_to_hex(hash);
+ crypto::public_key client;
+ uint64_t ts;
+#ifndef NDEBUG
+ if (nettype() == TESTNET && client_message == "debug")
+ {
+ credits = 0;
+ return true;
+ }
+#endif
+ if (!cryptonote::verify_rpc_payment_signature(client_message, client, ts))
+ {
+ credits = 0;
+ message = "Client signature does not verify for " + rpc;
+ return false;
+ }
+ crypto::public_key local_client;
+ if (!m_rpc_payment->pay(client, ts, payment, rpc, same_ts, credits))
+ {
+ message = CORE_RPC_STATUS_PAYMENT_REQUIRED;
+ return false;
+ }
+ return true;
+ }
+#define CHECK_PAYMENT_BASE(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P > 0 && !check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
+#define CHECK_PAYMENT(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, false)
+#define CHECK_PAYMENT_SAME_TS(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, true)
+#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::check_core_ready()
{
if(!m_p2p.get_payload_object().is_synchronized())
@@ -239,7 +373,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_height);
+ RPC_TRACKER(get_height);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HEIGHT>(invoke_http_mode::JON, "/getheight", req, res, r))
return r;
@@ -254,7 +388,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_info);
+ RPC_TRACKER(get_info);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r))
{
@@ -272,6 +406,8 @@ namespace cryptonote
return r;
}
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_GET_INFO, false);
+
const bool restricted = m_restricted && ctx;
crypto::hash top_hash;
@@ -330,7 +466,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_net_stats);
+ RPC_TRACKER(get_net_stats);
// No bootstrap daemon check: Only ever get stats about local server
res.start_time = (uint64_t)m_core.get_start_time();
{
@@ -357,20 +493,51 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_blocks);
+ RPC_TRACKER(get_blocks);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_FAST>(invoke_http_mode::BIN, "/getblocks.bin", req, res, r))
return r;
- std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
+ CHECK_PAYMENT(req, res, 1);
+
+ // quick check for noop
+ if (!req.block_ids.empty())
+ {
+ uint64_t last_block_height;
+ crypto::hash last_block_hash;
+ m_core.get_blockchain_top(last_block_height, last_block_hash);
+ if (last_block_hash == req.block_ids.front())
+ {
+ res.start_height = 0;
+ res.current_height = m_core.get_current_blockchain_height();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ }
- if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT;
+ if (m_rpc_payment)
+ {
+ max_blocks = res.credits / COST_PER_BLOCK;
+ if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)
+ max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT;
+ if (max_blocks == 0)
+ {
+ res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
+ return false;
+ }
+ }
+
+ std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks))
{
res.status = "Failed";
add_host_fail(ctx);
return false;
}
+ CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
+
size_t pruned_size = 0, unpruned_size = 0, ntxes = 0;
res.blocks.reserve(bs.size());
res.output_indices.reserve(bs.size());
@@ -422,7 +589,7 @@ namespace cryptonote
}
bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_alt_blocks_hashes);
+ RPC_TRACKER(get_alt_blocks_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_ALT_BLOCKS_HASHES>(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r))
return r;
@@ -449,7 +616,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_blocks_by_height);
+ RPC_TRACKER(get_blocks_by_height);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r))
return r;
@@ -457,6 +624,7 @@ namespace cryptonote
res.status = "Failed";
res.blocks.clear();
res.blocks.reserve(req.heights.size());
+ CHECK_PAYMENT_MIN1(req, res, req.heights.size() * COST_PER_BLOCK, false);
for (uint64_t height : req.heights)
{
block blk;
@@ -483,11 +651,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_hashes);
+ RPC_TRACKER(get_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HASHES_FAST>(invoke_http_mode::BIN, "/gethashes.bin", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
res.start_height = req.start_height;
if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, NULL, res.start_height, res.current_height, false))
{
@@ -496,17 +666,21 @@ namespace cryptonote
return false;
}
+ CHECK_PAYMENT_SAME_TS(req, res, res.m_block_ids.size() * COST_PER_BLOCK_HASH);
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_outs_bin);
+ RPC_TRACKER(get_outs_bin);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS_BIN>(invoke_http_mode::BIN, "/get_outs.bin", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, req.outputs.size() * COST_PER_OUT, false);
+
res.status = "Failed";
const bool restricted = m_restricted && ctx;
@@ -530,11 +704,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_outs);
+ RPC_TRACKER(get_outs);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS>(invoke_http_mode::JON, "/get_outs", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, req.outputs.size() * COST_PER_OUT, false);
+
res.status = "Failed";
const bool restricted = m_restricted && ctx;
@@ -574,11 +750,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_indexes);
+ RPC_TRACKER(get_indexes);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(invoke_http_mode::BIN, "/get_o_indexes.bin", req, res, ok))
return ok;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_OUTPUT_INDEXES, false);
+
bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes);
if(!r)
{
@@ -592,11 +770,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transactions);
+ RPC_TRACKER(get_transactions);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok))
return ok;
+ CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false);
+
std::vector<crypto::hash> vh;
for(const auto& tx_hex_str: req.txs_hashes)
{
@@ -818,13 +998,16 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_is_key_image_spent);
+ RPC_TRACKER(is_key_image_spent);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok))
return ok;
+ CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
+
std::vector<crypto::key_image> key_images;
for(const auto& ki_hex_str: req.key_images)
{
@@ -887,12 +1070,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_send_raw_tx);
+ RPC_TRACKER(send_raw_tx);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_SEND_RAW_TX>(invoke_http_mode::JON, "/sendrawtransaction", req, res, ok))
return ok;
CHECK_CORE_READY();
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_RELAY, false);
std::string tx_blob;
if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob))
@@ -966,7 +1150,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_start_mining);
+ RPC_TRACKER(start_mining);
CHECK_CORE_READY();
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, nettype(), req.miner_address))
@@ -1017,7 +1201,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_stop_mining);
+ RPC_TRACKER(stop_mining);
cryptonote::miner &miner= m_core.get_miner();
if(!miner.is_mining())
{
@@ -1037,7 +1221,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_mining_status);
+ RPC_TRACKER(mining_status);
const miner& lMiner = m_core.get_miner();
res.active = lMiner.is_mining();
@@ -1078,7 +1262,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_save_bc);
+ RPC_TRACKER(save_bc);
if( !m_core.get_blockchain_storage().store_blockchain() )
{
res.status = "Error while storing blockchain";
@@ -1090,7 +1274,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_peer_list);
+ RPC_TRACKER(get_peer_list);
std::vector<nodetool::peerlist_entry> white_list;
std::vector<nodetool::peerlist_entry> gray_list;
@@ -1107,24 +1291,24 @@ namespace cryptonote
{
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
- entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
- entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else
- res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
}
for (auto & entry : gray_list)
{
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
- entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
- entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else
- res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
}
res.status = CORE_RPC_STATUS_OK;
@@ -1133,7 +1317,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_public_nodes);
+ RPC_TRACKER(get_public_nodes);
COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx);
@@ -1172,7 +1356,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_log_hash_rate);
+ RPC_TRACKER(set_log_hash_rate);
if(m_core.get_miner().is_mining())
{
m_core.get_miner().do_print_hashrate(req.visible);
@@ -1187,7 +1371,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_log_level);
+ RPC_TRACKER(set_log_level);
if (req.level < 0 || req.level > 4)
{
res.status = "Error: log level not valid";
@@ -1200,7 +1384,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_log_categories);
+ RPC_TRACKER(set_log_categories);
mlog_set_log(req.categories.c_str());
res.categories = mlog_get_categories();
res.status = CORE_RPC_STATUS_OK;
@@ -1209,62 +1393,92 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool);
+ RPC_TRACKER(get_transaction_pool);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL>(invoke_http_mode::JON, "/get_transaction_pool", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
- m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !restricted);
- for (tx_info& txi : res.transactions)
- txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
+
+ size_t n_txes = m_core.get_pool_transactions_count();
+ if (n_txes > 0)
+ {
+ CHECK_PAYMENT_SAME_TS(req, res, n_txes * COST_PER_TX);
+ m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !restricted);
+ for (tx_info& txi : res.transactions)
+ txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
+ }
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool_hashes);
+ RPC_TRACKER(get_transaction_pool_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN>(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
- m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !restricted);
+
+ size_t n_txes = m_core.get_pool_transactions_count();
+ if (n_txes > 0)
+ {
+ CHECK_PAYMENT_SAME_TS(req, res, n_txes * COST_PER_POOL_HASH);
+ m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !restricted);
+ }
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool_hashes);
+ RPC_TRACKER(get_transaction_pool_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_HASHES>(invoke_http_mode::JON, "/get_transaction_pool_hashes", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
- std::vector<crypto::hash> tx_hashes;
- m_core.get_pool_transaction_hashes(tx_hashes, !request_has_rpc_origin || !restricted);
- res.tx_hashes.reserve(tx_hashes.size());
- for (const crypto::hash &tx_hash: tx_hashes)
- res.tx_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash));
+
+ size_t n_txes = m_core.get_pool_transactions_count();
+ if (n_txes > 0)
+ {
+ CHECK_PAYMENT_SAME_TS(req, res, n_txes * COST_PER_POOL_HASH);
+ std::vector<crypto::hash> tx_hashes;
+ m_core.get_pool_transaction_hashes(tx_hashes, !request_has_rpc_origin || !restricted);
+ res.tx_hashes.reserve(tx_hashes.size());
+ for (const crypto::hash &tx_hash: tx_hashes)
+ res.tx_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash));
+ }
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool_stats);
+ RPC_TRACKER(get_transaction_pool_stats);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_STATS>(invoke_http_mode::JON, "/get_transaction_pool_stats", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_POOL_STATS, false);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !restricted);
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -1293,7 +1507,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_stop_daemon);
+ RPC_TRACKER(stop_daemon);
// FIXME: replace back to original m_p2p.send_stop_signal() after
// investigating why that isn't working quite right.
m_p2p.send_stop_signal();
@@ -1303,7 +1517,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_getblockcount);
+ RPC_TRACKER(getblockcount);
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (m_should_use_bootstrap_daemon)
@@ -1319,7 +1533,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_getblockhash);
+ RPC_TRACKER(getblockhash);
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (m_should_use_bootstrap_daemon)
@@ -1361,9 +1575,65 @@ namespace cryptonote
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
+ {
+ b = boost::value_initialized<cryptonote::block>();
+ if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to create block template");
+ return false;
+ }
+ blobdata block_blob = t_serializable_object_to_blob(b);
+ crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
+ if(tx_pub_key == crypto::null_pkey)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to get tx pub key in coinbase extra");
+ return false;
+ }
+
+ if (b.major_version >= RX_BLOCK_VERSION)
+ {
+ uint64_t next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+ seed_hash = m_core.get_block_id_by_height(seed_height);
+ if (next_height != seed_height)
+ next_seed_hash = m_core.get_block_id_by_height(next_height);
+ else
+ next_seed_hash = seed_hash;
+ }
+
+ if (extra_nonce.empty())
+ {
+ reserved_offset = 0;
+ return true;
+ }
+
+ reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
+ if(!reserved_offset)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to find tx pub key in blockblob");
+ return false;
+ }
+ reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ if(reserved_offset + extra_nonce.size() > block_blob.size())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to calculate offset for ");
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_getblocktemplate);
+ RPC_TRACKER(getblocktemplate);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GETBLOCKTEMPLATE>(invoke_http_mode::JON_RPC, "getblocktemplate", req, res, r))
return r;
@@ -1413,6 +1683,7 @@ namespace cryptonote
block b;
cryptonote::blobdata blob_reserve;
+ size_t reserved_offset;
if(!req.extra_nonce.empty())
{
if(!string_tools::parse_hexstr_to_binbuff(req.extra_nonce, blob_reserve))
@@ -1435,54 +1706,20 @@ namespace cryptonote
return false;
}
}
- if(!m_core.get_block_template(b, req.prev_block.empty() ? NULL : &prev_block, info.address, wdiff, res.height, res.expected_reward, blob_reserve))
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to create block template");
+ uint64_t seed_height;
+ crypto::hash seed_hash, next_seed_hash;
+ if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, b, res.seed_height, seed_hash, next_seed_hash, error_resp))
return false;
- }
if (b.major_version >= RX_BLOCK_VERSION)
{
- uint64_t seed_height, next_height;
- crypto::hash seed_hash;
- crypto::rx_seedheights(res.height, &seed_height, &next_height);
- seed_hash = m_core.get_block_id_by_height(seed_height);
res.seed_hash = string_tools::pod_to_hex(seed_hash);
- if (next_height != seed_height) {
- seed_hash = m_core.get_block_id_by_height(next_height);
- res.next_seed_hash = string_tools::pod_to_hex(seed_hash);
- }
+ if (seed_hash != next_seed_hash)
+ res.next_seed_hash = string_tools::pod_to_hex(next_seed_hash);
}
+
+ res.reserved_offset = reserved_offset;
store_difficulty(wdiff, res.difficulty, res.wide_difficulty, res.difficulty_top64);
blobdata block_blob = t_serializable_object_to_blob(b);
- crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
- if(tx_pub_key == crypto::null_pkey)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to get tx pub key in coinbase extra");
- return false;
- }
- res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
- if(!res.reserved_offset)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to find tx pub key in blockblob");
- return false;
- }
- if (req.reserve_size)
- res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
- else
- res.reserved_offset = 0;
- if(res.reserved_offset + req.reserve_size > block_blob.size())
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to calculate offset for ");
- return false;
- }
blobdata hashing_blob = get_block_hashing_blob(b);
res.prev_hash = string_tools::pod_to_hex(b.prev_id);
res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
@@ -1493,7 +1730,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_submitblock);
+ RPC_TRACKER(submitblock);
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (m_should_use_bootstrap_daemon)
@@ -1549,7 +1786,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_generateblocks);
+ RPC_TRACKER(generateblocks);
CHECK_CORE_READY();
@@ -1724,12 +1961,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_last_block_header);
+ RPC_TRACKER(get_last_block_header);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LAST_BLOCK_HEADER>(invoke_http_mode::JON_RPC, "getlastblockheader", req, res, r))
return r;
CHECK_CORE_READY();
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
uint64_t last_block_height;
crypto::hash last_block_hash;
m_core.get_blockchain_top(last_block_height, last_block_hash);
@@ -1755,11 +1993,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block_header_by_hash);
+ RPC_TRACKER(get_block_header_by_hash);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
+
auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool {
crypto::hash block_hash;
bool hash_parsed = parse_hash256(hash, block_hash);
@@ -1815,7 +2055,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block_headers_range(const COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request& req, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block_headers_range);
+ RPC_TRACKER(get_block_headers_range);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADERS_RANGE>(invoke_http_mode::JON_RPC, "getblockheadersrange", req, res, r))
return r;
@@ -1827,6 +2067,7 @@ namespace cryptonote
error_resp.message = "Invalid start/end heights.";
return false;
}
+ CHECK_PAYMENT_MIN1(req, res, (req.end_height - req.start_height + 1) * COST_PER_BLOCK_HEADER, false);
for (uint64_t h = req.start_height; h <= req.end_height; ++h)
{
crypto::hash block_hash = m_core.get_block_id_by_height(h);
@@ -1867,7 +2108,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block_header_by_height);
+ RPC_TRACKER(get_block_header_by_height);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT>(invoke_http_mode::JON_RPC, "getblockheaderbyheight", req, res, r))
return r;
@@ -1878,6 +2119,7 @@ namespace cryptonote
error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
return false;
}
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
block blk;
bool have_block = m_core.get_block_by_hash(block_hash, blk);
@@ -1901,11 +2143,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block(const COMMAND_RPC_GET_BLOCK::request& req, COMMAND_RPC_GET_BLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block);
+ RPC_TRACKER(get_block);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK>(invoke_http_mode::JON_RPC, "getblock", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK, false);
+
crypto::hash block_hash;
if (!req.hash.empty())
{
@@ -1963,7 +2207,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_connections);
+ RPC_TRACKER(get_connections);
res.connections = m_p2p.get_payload_object().get_connections();
@@ -1974,16 +2218,24 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- return on_get_info(req, res, ctx);
+ on_get_info(req, res, ctx);
+ if (res.status != CORE_RPC_STATUS_OK)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = res.status;
+ return false;
+ }
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_hard_fork_info);
+ RPC_TRACKER(hard_fork_info);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_HARD_FORK_INFO>(invoke_http_mode::JON_RPC, "hard_fork_info", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, COST_PER_HARD_FORK_INFO);
const Blockchain &blockchain = m_core.get_blockchain_storage();
uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version();
res.version = blockchain.get_current_hard_fork_version();
@@ -1995,7 +2247,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_bans);
+ RPC_TRACKER(get_bans);
auto now = time(nullptr);
std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
@@ -2059,7 +2311,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_set_bans);
+ RPC_TRACKER(set_bans);
for (auto i = req.bans.begin(); i != req.bans.end(); ++i)
{
@@ -2107,7 +2359,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_flush_txpool);
+ RPC_TRACKER(flush_txpool);
bool failed = false;
std::vector<crypto::hash> txids;
@@ -2162,12 +2414,22 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_output_histogram);
+ RPC_TRACKER(get_output_histogram);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_HISTOGRAM>(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r))
return r;
const bool restricted = m_restricted && ctx;
+ size_t amounts = req.amounts.size();
+ if (restricted && amounts == 0)
+ {
+ res.status = "Restricted RPC will not serve histograms on the whole blockchain. Use your own node.";
+ return true;
+ }
+
+ uint64_t cost = req.amounts.empty() ? COST_PER_FULL_OUTPUT_HISTOGRAM : (COST_PER_OUTPUT_HISTOGRAM * amounts);
+ CHECK_PAYMENT_MIN1(req, res, cost, false);
+
if (restricted && req.recent_cutoff > 0 && req.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION)
{
res.status = "Recent cutoff is too old";
@@ -2199,7 +2461,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_version);
+ RPC_TRACKER(get_version);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_VERSION>(invoke_http_mode::JON_RPC, "get_version", req, res, r))
return r;
@@ -2212,7 +2474,14 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_coinbase_tx_sum);
+ RPC_TRACKER(get_coinbase_tx_sum);
+ const uint64_t bc_height = m_core.get_current_blockchain_height();
+ if (req.height >= bc_height || req.count > bc_height)
+ {
+ res.status = "height or count is too large";
+ return true;
+ }
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_COINBASE_TX_SUM_BLOCK * req.count, false);
std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count);
res.emission_amount = amounts.first;
res.fee_amount = amounts.second;
@@ -2222,11 +2491,12 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_base_fee_estimate);
+ RPC_TRACKER(get_base_fee_estimate);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BASE_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks);
res.quantization_mask = Blockchain::get_fee_quantization_mask();
res.status = CORE_RPC_STATUS_OK;
@@ -2235,7 +2505,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_alternate_chains);
+ RPC_TRACKER(get_alternate_chains);
try
{
std::vector<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
@@ -2268,7 +2538,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_limit);
+ RPC_TRACKER(get_limit);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LIMIT>(invoke_http_mode::JON, "/get_limit", req, res, r))
return r;
@@ -2281,7 +2551,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_limit);
+ RPC_TRACKER(set_limit);
// -1 = reset to default
// 0 = do not modify
@@ -2321,7 +2591,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_out_peers);
+ RPC_TRACKER(out_peers);
if (req.set)
m_p2p.change_max_out_public_peers(req.out_peers);
res.out_peers = m_p2p.get_max_out_public_peers();
@@ -2331,7 +2601,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_in_peers);
+ RPC_TRACKER(in_peers);
if (req.set)
m_p2p.change_max_in_public_peers(req.in_peers);
res.in_peers = m_p2p.get_max_in_public_peers();
@@ -2341,7 +2611,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_update);
+ RPC_TRACKER(update);
if (m_core.offline())
{
@@ -2443,7 +2713,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_pop_blocks);
+ RPC_TRACKER(pop_blocks);
m_core.get_blockchain_storage().pop_blocks(req.nblocks);
@@ -2455,7 +2725,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_relay_tx);
+ RPC_TRACKER(relay_tx);
+ CHECK_PAYMENT_MIN1(req, res, req.txids.size() * COST_PER_TX_RELAY, false);
bool failed = false;
res.status = "";
@@ -2501,7 +2772,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_sync_info);
+ RPC_TRACKER(sync_info);
+ CHECK_PAYMENT(req, res, COST_PER_SYNC_INFO);
crypto::hash top_hash;
m_core.get_blockchain_top(res.height, top_hash);
@@ -2530,10 +2802,12 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_txpool_backlog);
+ RPC_TRACKER(get_txpool_backlog);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG>(invoke_http_mode::JON_RPC, "get_txpool_backlog", req, res, r))
return r;
+ size_t n_txes = m_core.get_pool_transactions_count();
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_POOL_STATS * n_txes, false);
if (!m_core.get_txpool_backlog(res.backlog))
{
@@ -2548,11 +2822,16 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_output_distribution);
+ RPC_TRACKER(get_output_distribution);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::JON_RPC, "get_output_distribution", req, res, r))
return r;
+ size_t n_0 = 0, n_non0 = 0;
+ for (uint64_t amount: req.amounts)
+ if (amount) ++n_non0; else ++n_0;
+ CHECK_PAYMENT_MIN1(req, res, n_0 * COST_PER_OUTPUT_DISTRIBUTION_0 + n_non0 * COST_PER_OUTPUT_DISTRIBUTION, false);
+
try
{
// 0 is placeholder for the whole chain
@@ -2583,12 +2862,17 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_output_distribution_bin);
+ RPC_TRACKER(get_output_distribution_bin);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::BIN, "/get_output_distribution.bin", req, res, r))
return r;
+ size_t n_0 = 0, n_non0 = 0;
+ for (uint64_t amount: req.amounts)
+ if (amount) ++n_non0; else ++n_0;
+ CHECK_PAYMENT_MIN1(req, res, n_0 * COST_PER_OUTPUT_DISTRIBUTION_0 + n_non0 * COST_PER_OUTPUT_DISTRIBUTION, false);
+
res.status = "Failed";
if (!req.binary)
@@ -2624,6 +2908,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_prune_blockchain(const COMMAND_RPC_PRUNE_BLOCKCHAIN::request& req, COMMAND_RPC_PRUNE_BLOCKCHAIN::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
+ RPC_TRACKER(prune_blockchain);
+
try
{
if (!(req.check ? m_core.check_blockchain_pruning() : m_core.prune_blockchain()))
@@ -2641,13 +2927,248 @@ namespace cryptonote
error_resp.message = "Failed to prune blockchain";
return false;
}
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_info(const COMMAND_RPC_ACCESS_INFO::request& req, COMMAND_RPC_ACCESS_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_info);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_INFO>(invoke_http_mode::JON, "rpc_access_info", req, res, r))
+ return r;
+
+ // if RPC payment is not enabled
+ if (m_rpc_payment == NULL)
+ {
+ res.diff = 0;
+ res.credits_per_hash_found = 0;
+ res.credits = 0;
+ res.height = 0;
+ res.seed_height = 0;
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+
+ crypto::public_key client;
+ uint64_t ts;
+ if (!cryptonote::verify_rpc_payment_signature(req.client, client, ts))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ crypto::hash top_hash;
+ m_core.get_blockchain_top(res.height, top_hash);
+ ++res.height;
+ cryptonote::blobdata hashing_blob;
+ crypto::hash seed_hash, next_seed_hash;
+ if (!m_rpc_payment->get_info(client, [&](const cryptonote::blobdata &extra_nonce, cryptonote::block &b, uint64_t &seed_height, crypto::hash &seed_hash)->bool{
+ cryptonote::difficulty_type difficulty;
+ uint64_t height, expected_reward;
+ size_t reserved_offset;
+ if (!get_block_template(m_rpc_payment->get_payment_address(), NULL, extra_nonce, reserved_offset, difficulty, height, expected_reward, b, seed_height, seed_hash, next_seed_hash, error_resp))
+ return false;
+ return true;
+ }, hashing_blob, res.seed_height, seed_hash, top_hash, res.diff, res.credits_per_hash_found, res.credits, res.cookie))
+ {
+ return false;
+ }
+ if (hashing_blob.empty())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
+ error_resp.message = "Invalid hashing blob";
+ return false;
+ }
+ res.hashing_blob = epee::string_tools::buff_to_hex_nodelimer(hashing_blob);
+ res.top_hash = epee::string_tools::pod_to_hex(top_hash);
+ if (hashing_blob[0] >= RX_BLOCK_VERSION)
+ {
+ res.seed_hash = string_tools::pod_to_hex(seed_hash);
+ if (seed_hash != next_seed_hash)
+ res.next_seed_hash = string_tools::pod_to_hex(next_seed_hash);
+ }
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_submit_nonce(const COMMAND_RPC_ACCESS_SUBMIT_NONCE::request& req, COMMAND_RPC_ACCESS_SUBMIT_NONCE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_submit_nonce);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_SUBMIT_NONCE>(invoke_http_mode::JON, "rpc_access_submit_nonce", req, res, r))
+ return r;
+ // if RPC payment is not enabled
+ if (m_rpc_payment == NULL)
+ {
+ res.status = "Payment not necessary";
+ return true;
+ }
+
+ crypto::public_key client;
+ uint64_t ts;
+ if (!cryptonote::verify_rpc_payment_signature(req.client, client, ts))
+ {
+ res.credits = 0;
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ crypto::hash hash;
+ cryptonote::block block;
+ crypto::hash top_hash;
+ uint64_t height;
+ bool stale;
+ m_core.get_blockchain_top(height, top_hash);
+ if (!m_rpc_payment->submit_nonce(client, req.nonce, top_hash, error_resp.code, error_resp.message, res.credits, hash, block, req.cookie, stale))
+ {
+ return false;
+ }
+
+ if (!stale)
+ {
+ // it might be a valid block!
+ const difficulty_type current_difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block();
+ if (check_hash(hash, current_difficulty))
+ {
+ MINFO("This payment meets the current network difficulty");
+ block_verification_context bvc;
+ if(m_core.handle_block_found(block, bvc))
+ MGINFO_GREEN("Block found by RPC user at height " << get_block_height(block) << ": " <<
+ print_money(cryptonote::get_outs_money_amount(block.miner_tx)));
+ else
+ MERROR("Seemingly valid block was not accepted");
+ }
+ }
+
+ m_core.get_blockchain_top(height, top_hash);
+ res.top_hash = epee::string_tools::pod_to_hex(top_hash);
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_pay(const COMMAND_RPC_ACCESS_PAY::request& req, COMMAND_RPC_ACCESS_PAY::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_pay);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_PAY>(invoke_http_mode::JON, "rpc_access_pay", req, res, r))
+ return r;
+
+ // if RPC payment is not enabled
+ if (m_rpc_payment == NULL)
+ {
+ res.status = "Payment not necessary";
+ return true;
+ }
+
+ crypto::public_key client;
+ uint64_t ts;
+ if (!cryptonote::verify_rpc_payment_signature(req.client, client, ts))
+ {
+ res.credits = 0;
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ RPCTracker ext_tracker(("external:" + req.paying_for).c_str(), PERF_TIMER_NAME(rpc_access_pay));
+ if (!check_payment(req.client, req.payment, req.paying_for, false, res.status, res.credits, res.top_hash))
+ return true;
+ ext_tracker.pay(req.payment);
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_tracking(const COMMAND_RPC_ACCESS_TRACKING::request& req, COMMAND_RPC_ACCESS_TRACKING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_tracking);
+
+ if (req.clear)
+ {
+ RPCTracker::clear();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+
+ auto data = RPCTracker::data();
+ for (const auto &d: data)
+ {
+ res.data.resize(res.data.size() + 1);
+ res.data.back().rpc = d.first;
+ res.data.back().count = d.second.count;
+ res.data.back().time = d.second.time;
+ res.data.back().credits = d.second.credits;
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_data(const COMMAND_RPC_ACCESS_DATA::request& req, COMMAND_RPC_ACCESS_DATA::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_data);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_DATA>(invoke_http_mode::JON, "rpc_access_data", req, res, r))
+ return r;
+
+ if (!m_rpc_payment)
+ {
+ res.status = "Payments not enabled";
+ return false;
+ }
+
+ m_rpc_payment->foreach([&](const crypto::public_key &client, const rpc_payment::client_info &info){
+ res.entries.push_back({
+ epee::string_tools::pod_to_hex(client), info.credits, std::max(info.last_request_timestamp / 1000000, info.update_time),
+ info.credits_total, info.credits_used, info.nonces_good, info.nonces_stale, info.nonces_bad, info.nonces_dupe
+ });
+ return true;
+ });
+
+ res.hashrate = m_rpc_payment->get_hashes(600) / 600;
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_account(const COMMAND_RPC_ACCESS_ACCOUNT::request& req, COMMAND_RPC_ACCESS_ACCOUNT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_account);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_ACCOUNT>(invoke_http_mode::JON, "rpc_access_account", req, res, r))
+ return r;
+
+ if (!m_rpc_payment)
+ {
+ res.status = "Payments not enabled";
+ return false;
+ }
+
+ crypto::public_key client;
+ if (!epee::string_tools::hex_to_pod(req.client.substr(0, 2 * sizeof(client)), client))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ res.credits = m_rpc_payment->balance(client, req.delta_balance);
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
const command_line::arg_descriptor<std::string, false, true, 2> core_rpc_server::arg_rpc_bind_port = {
"rpc-bind-port"
, "Port for RPC server"
@@ -2686,4 +3207,22 @@ namespace cryptonote
, "Specify username:password for the bootstrap daemon login"
, ""
};
+
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_payment_address = {
+ "rpc-payment-address"
+ , "Restrict RPC to clients sending micropayment to this address"
+ , ""
+ };
+
+ const command_line::arg_descriptor<uint64_t> core_rpc_server::arg_rpc_payment_difficulty = {
+ "rpc-payment-difficulty"
+ , "Restrict RPC to clients sending micropayment at this difficulty"
+ , DEFAULT_PAYMENT_DIFFICULTY
+ };
+
+ const command_line::arg_descriptor<uint64_t> core_rpc_server::arg_rpc_payment_credits = {
+ "rpc-payment-credits"
+ , "Restrict RPC to clients sending micropayment, yields that many credits per payment"
+ , DEFAULT_PAYMENT_CREDITS_PER_HASH
+ };
} // namespace cryptonote