aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/core_rpc_server.cpp
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2018-02-11 15:15:56 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-10-25 09:34:38 +0000
commit2899379791b7542e4eb920b5d9d58cf232806937 (patch)
tree7e97a95103837852d5c4e86ee544815d9a7a6671 /src/rpc/core_rpc_server.cpp
parentadd a quick early out to get_blocks.bin when up to date (diff)
downloadmonero-2899379791b7542e4eb920b5d9d58cf232806937.tar.xz
daemon, wallet: new pay for RPC use system
Daemons intended for public use can be set up to require payment in the form of hashes in exchange for RPC service. This enables public daemons to receive payment for their work over a large number of calls. This system behaves similarly to a pool, so payment takes the form of valid blocks every so often, yielding a large one off payment, rather than constant micropayments. This system can also be used by third parties as a "paywall" layer, where users of a service can pay for use by mining Monero to the service provider's address. An example of this for web site access is Primo, a Monero mining based website "paywall": https://github.com/selene-kovri/primo This has some advantages: - incentive to run a node providing RPC services, thereby promoting the availability of third party nodes for those who can't run their own - incentive to run your own node instead of using a third party's, thereby promoting decentralization - decentralized: payment is done between a client and server, with no third party needed - private: since the system is "pay as you go", you don't need to identify yourself to claim a long lived balance - no payment occurs on the blockchain, so there is no extra transactional load - one may mine with a beefy server, and use those credits from a phone, by reusing the client ID (at the cost of some privacy) - no barrier to entry: anyone may run a RPC node, and your expected revenue depends on how much work you do - Sybil resistant: if you run 1000 idle RPC nodes, you don't magically get more revenue - no large credit balance maintained on servers, so they have no incentive to exit scam - you can use any/many node(s), since there's little cost in switching servers - market based prices: competition between servers to lower costs - incentive for a distributed third party node system: if some public nodes are overused/slow, traffic can move to others - increases network security - helps counteract mining pools' share of the network hash rate - zero incentive for a payer to "double spend" since a reorg does not give any money back to the miner And some disadvantages: - low power clients will have difficulty mining (but one can optionally mine in advance and/or with a faster machine) - payment is "random", so a server might go a long time without a block before getting one - a public node's overall expected payment may be small Public nodes are expected to compete to find a suitable level for cost of service. The daemon can be set up this way to require payment for RPC services: monerod --rpc-payment-address 4xxxxxx \ --rpc-payment-credits 250 --rpc-payment-difficulty 1000 These values are an example only. The --rpc-payment-difficulty switch selects how hard each "share" should be, similar to a mining pool. The higher the difficulty, the fewer shares a client will find. The --rpc-payment-credits switch selects how many credits are awarded for each share a client finds. Considering both options, clients will be awarded credits/difficulty credits for every hash they calculate. For example, in the command line above, 0.25 credits per hash. A client mining at 100 H/s will therefore get an average of 25 credits per second. For reference, in the current implementation, a credit is enough to sync 20 blocks, so a 100 H/s client that's just starting to use Monero and uses this daemon will be able to sync 500 blocks per second. The wallet can be set to automatically mine if connected to a daemon which requires payment for RPC usage. It will try to keep a balance of 50000 credits, stopping mining when it's at this level, and starting again as credits are spent. With the example above, a new client will mine this much credits in about half an hour, and this target is enough to sync 500000 blocks (currently about a third of the monero blockchain). There are three new settings in the wallet: - credits-target: this is the amount of credits a wallet will try to reach before stopping mining. The default of 0 means 50000 credits. - auto-mine-for-rpc-payment-threshold: this controls the minimum credit rate which the wallet considers worth mining for. If the daemon credits less than this ratio, the wallet will consider mining to be not worth it. In the example above, the rate is 0.25 - persistent-rpc-client-id: if set, this allows the wallet to reuse a client id across runs. This means a public node can tell a wallet that's connecting is the same as one that connected previously, but allows a wallet to keep their credit balance from one run to the other. Since the wallet only mines to keep a small credit balance, this is not normally worth doing. However, someone may want to mine on a fast server, and use that credit balance on a low power device such as a phone. If left unset, a new client ID is generated at each wallet start, for privacy reasons. To mine and use a credit balance on two different devices, you can use the --rpc-client-secret-key switch. A wallet's client secret key can be found using the new rpc_payments command in the wallet. Note: anyone knowing your RPC client secret key is able to use your credit balance. The wallet has a few new commands too: - start_mining_for_rpc: start mining to acquire more credits, regardless of the auto mining settings - stop_mining_for_rpc: stop mining to acquire more credits - rpc_payments: display information about current credits with the currently selected daemon The node has an extra command: - rpc_payments: display information about clients and their balances The node will forget about any balance for clients which have been inactive for 6 months. Balances carry over on node restart.
Diffstat (limited to 'src/rpc/core_rpc_server.cpp')
-rw-r--r--src/rpc/core_rpc_server.cpp757
1 files changed, 641 insertions, 116 deletions
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index bc7fe918f..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,11 +493,13 @@ 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;
+ CHECK_PAYMENT(req, res, 1);
+
// quick check for noop
if (!req.block_ids.empty())
{
@@ -377,14 +515,29 @@ namespace cryptonote
}
}
+ 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, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ 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());
@@ -436,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;
@@ -463,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;
@@ -471,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;
@@ -497,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))
{
@@ -510,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;
@@ -544,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;
@@ -588,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)
{
@@ -606,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)
{
@@ -832,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)
{
@@ -901,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))
@@ -980,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))
@@ -1031,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())
{
@@ -1051,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();
@@ -1092,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";
@@ -1104,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;
@@ -1121,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;
@@ -1147,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);
@@ -1186,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);
@@ -1201,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";
@@ -1214,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;
@@ -1223,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;
}
@@ -1307,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();
@@ -1317,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)
@@ -1333,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)
@@ -1375,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;
@@ -1427,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))
@@ -1449,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);
@@ -1507,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)
@@ -1563,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();
@@ -1738,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);
@@ -1769,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);
@@ -1829,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;
@@ -1841,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);
@@ -1881,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;
@@ -1892,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);
@@ -1915,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())
{
@@ -1977,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();
@@ -1988,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();
@@ -2009,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();
@@ -2073,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)
{
@@ -2121,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;
@@ -2176,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";
@@ -2213,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;
@@ -2226,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;
@@ -2236,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;
@@ -2249,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();
@@ -2282,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;
@@ -2295,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
@@ -2335,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();
@@ -2345,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();
@@ -2355,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())
{
@@ -2457,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);
@@ -2469,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 = "";
@@ -2515,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);
@@ -2544,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))
{
@@ -2562,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
@@ -2597,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)
@@ -2638,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()))
@@ -2655,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"
@@ -2700,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