diff options
author | luigi1111 <luigi1111w@gmail.com> | 2019-10-25 13:38:21 -0500 |
---|---|---|
committer | luigi1111 <luigi1111w@gmail.com> | 2019-10-25 13:38:21 -0500 |
commit | 960c2158010d30a375207310a36a7a942b9285d2 (patch) | |
tree | 46d25ec0db661fd62fd1a683dea3b1ecbf620089 /src/rpc | |
parent | Merge pull request #6002 (diff) | |
parent | simplewallet: add public_nodes command (diff) | |
download | monero-960c2158010d30a375207310a36a7a942b9285d2.tar.xz |
Merge pull request #5357
b3a9a4d add a quick early out to get_blocks.bin when up to date (moneromooo-monero)
2899379 daemon, wallet: new pay for RPC use system (moneromooo-monero)
ffa4602 simplewallet: add public_nodes command (moneromooo-monero)
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 773 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.h | 23 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 804 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_error_codes.h | 29 | ||||
-rw-r--r-- | src/rpc/message_data_structs.h | 1 | ||||
-rw-r--r-- | src/rpc/rpc_payment.cpp | 402 | ||||
-rw-r--r-- | src/rpc/rpc_payment.h | 146 | ||||
-rw-r--r-- | src/rpc/rpc_payment_costs.h | 50 | ||||
-rw-r--r-- | src/rpc/rpc_payment_signature.cpp | 107 | ||||
-rw-r--r-- | src/rpc/rpc_payment_signature.h | 39 |
11 files changed, 1950 insertions, 435 deletions
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 116e7f568..ebb1e767f 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -29,12 +29,14 @@ include_directories(SYSTEM ${ZMQ_INCLUDE_PATH}) set(rpc_base_sources - rpc_args.cpp) + rpc_args.cpp + rpc_payment_signature.cpp + rpc_handler.cpp) set(rpc_sources bootstrap_daemon.cpp core_rpc_server.cpp - rpc_handler.cpp + rpc_payment.cpp instanciations) set(daemon_messages_sources @@ -47,7 +49,9 @@ set(daemon_rpc_server_sources set(rpc_base_headers - rpc_args.h) + rpc_args.h + rpc_payment_signature.h + rpc_handler.h) set(rpc_headers rpc_handler.h) @@ -58,6 +62,7 @@ set(daemon_rpc_server_headers) set(rpc_daemon_private_headers bootstrap_daemon.h core_rpc_server.h + rpc_payment.h core_rpc_server_commands_defs.h core_rpc_server_error_codes.h) 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 diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index fe03012b7..6655d621e 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -42,6 +42,7 @@ #include "cryptonote_core/cryptonote_core.h" #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "rpc_payment.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" @@ -71,6 +72,9 @@ namespace cryptonote static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login; + static const command_line::arg_descriptor<std::string> arg_rpc_payment_address; + static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty; + static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits; typedef epee::net_utils::connection_context_base connection_context; @@ -78,6 +82,7 @@ namespace cryptonote core& cr , nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p ); + ~core_rpc_server(); static void init_options(boost::program_options::options_description& desc); bool init( @@ -111,7 +116,7 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted) MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted) MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted) - MAP_URI_AUTO_JON2_IF("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES, !m_restricted) + MAP_URI_AUTO_JON2("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES) MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted) MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted) MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted) @@ -169,6 +174,12 @@ namespace cryptonote MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG) MAP_JON_RPC_WE("get_output_distribution", on_get_output_distribution, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION) MAP_JON_RPC_WE_IF("prune_blockchain", on_prune_blockchain, COMMAND_RPC_PRUNE_BLOCKCHAIN, !m_restricted) + MAP_JON_RPC_WE("rpc_access_info", on_rpc_access_info, COMMAND_RPC_ACCESS_INFO) + MAP_JON_RPC_WE("rpc_access_submit_nonce",on_rpc_access_submit_nonce, COMMAND_RPC_ACCESS_SUBMIT_NONCE) + MAP_JON_RPC_WE("rpc_access_pay", on_rpc_access_pay, COMMAND_RPC_ACCESS_PAY) + MAP_JON_RPC_WE_IF("rpc_access_tracking", on_rpc_access_tracking, COMMAND_RPC_ACCESS_TRACKING, !m_restricted) + MAP_JON_RPC_WE_IF("rpc_access_data", on_rpc_access_data, COMMAND_RPC_ACCESS_DATA, !m_restricted) + MAP_JON_RPC_WE_IF("rpc_access_account", on_rpc_access_account, COMMAND_RPC_ACCESS_ACCOUNT, !m_restricted) END_JSON_RPC_MAP() END_URI_MAP2() @@ -236,6 +247,12 @@ namespace cryptonote bool 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 = NULL); bool 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 = NULL); bool 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 = NULL); + bool 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 = NULL); + bool 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 = NULL); + bool 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 = NULL); + bool 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 = NULL); + bool 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 = NULL); + bool 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 = NULL); //----------------------- private: @@ -252,6 +269,8 @@ private: enum invoke_http_mode { JON, BIN, JON_RPC }; template <typename COMMAND_TYPE> bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r); + bool 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); + bool check_payment(const std::string &client, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash); core& m_core; nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p; @@ -260,10 +279,10 @@ private: bool m_should_use_bootstrap_daemon; std::chrono::system_clock::time_point m_bootstrap_height_check_time; bool m_was_bootstrap_ever_used; - network_type m_nettype; bool m_restricted; epee::critical_section m_host_fails_score_lock; std::map<std::string, uint64_t> m_host_fails_score; + std::unique_ptr<rpc_payment> m_rpc_payment; }; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 2760260f6..89c88a835 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -78,6 +78,7 @@ namespace cryptonote #define CORE_RPC_STATUS_OK "OK" #define CORE_RPC_STATUS_BUSY "BUSY" #define CORE_RPC_STATUS_NOT_MINING "NOT MINING" +#define CORE_RPC_STATUS_PAYMENT_REQUIRED "PAYMENT REQUIRED" // When making *any* change here, bump minor // If the change is incompatible, then bump major and set minor to 0 @@ -91,26 +92,67 @@ namespace cryptonote #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) + struct rpc_request_base + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct rpc_response_base + { + std::string status; + bool untrusted; + + rpc_response_base(): untrusted(false) {} + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) + END_KV_SERIALIZE_MAP() + }; + + struct rpc_access_request_base: public rpc_request_base + { + std::string client; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) + KV_SERIALIZE(client) + END_KV_SERIALIZE_MAP() + }; + + struct rpc_access_response_base: public rpc_response_base + { + uint64_t credits; + std::string top_hash; + + rpc_access_response_base(): credits(0) {} + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) + KV_SERIALIZE(credits) + KV_SERIALIZE(top_hash) + END_KV_SERIALIZE_MAP() + }; + struct COMMAND_RPC_GET_HEIGHT { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { uint64_t height; - std::string status; - bool untrusted; std::string hash; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(height) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) KV_SERIALIZE(hash) END_KV_SERIALIZE_MAP() }; @@ -120,13 +162,14 @@ namespace cryptonote struct COMMAND_RPC_GET_BLOCKS_FAST { - struct request_t + struct request_t: public rpc_access_request_base { std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ uint64_t start_height; bool prune; bool no_miner_tx; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) KV_SERIALIZE(start_height) KV_SERIALIZE(prune) @@ -153,22 +196,19 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<block_complete_entry> blocks; uint64_t start_height; uint64_t current_height; - std::string status; std::vector<block_output_indices> output_indices; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(blocks) KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) - KV_SERIALIZE(status) KV_SERIALIZE(output_indices) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -176,25 +216,23 @@ namespace cryptonote struct COMMAND_RPC_GET_BLOCKS_BY_HEIGHT { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<uint64_t> heights; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(heights) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<block_complete_entry> blocks; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(blocks) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -202,23 +240,21 @@ namespace cryptonote struct COMMAND_RPC_GET_ALT_BLOCKS_HASHES { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<std::string> blks_hashes; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(blks_hashes) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -226,31 +262,29 @@ namespace cryptonote struct COMMAND_RPC_GET_HASHES_FAST { - struct request_t + struct request_t: public rpc_access_request_base { std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ uint64_t start_height; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) KV_SERIALIZE(start_height) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<crypto::hash> m_block_ids; uint64_t start_height; uint64_t current_height; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -288,7 +322,7 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_GET_TRANSACTIONS { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<std::string> txs_hashes; bool decode_as_json; @@ -296,6 +330,7 @@ namespace cryptonote bool split; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(txs_hashes) KV_SERIALIZE(decode_as_json) KV_SERIALIZE_OPT(prune, false) @@ -341,7 +376,7 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_access_response_base { // older compatibility stuff std::vector<std::string> txs_as_hex; //transactions blobs as hex (old compat) @@ -352,16 +387,13 @@ namespace cryptonote // new style std::vector<entry> txs; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(txs_as_hex) KV_SERIALIZE(txs_as_json) KV_SERIALIZE(txs) KV_SERIALIZE(missed_tx) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -376,27 +408,25 @@ namespace cryptonote SPENT_IN_POOL = 2, }; - struct request_t + struct request_t: public rpc_access_request_base { std::vector<std::string> key_images; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(key_images) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<int> spent_status; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(spent_status) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -405,25 +435,24 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES { - struct request_t + struct request_t: public rpc_access_request_base { crypto::hash txid; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE_VAL_POD_AS_BLOB(txid) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<uint64_t> o_indexes; - std::string status; - bool untrusted; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(o_indexes) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -442,12 +471,13 @@ namespace cryptonote struct COMMAND_RPC_GET_OUTPUTS_BIN { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<get_outputs_out> outputs; bool get_txid; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(outputs) KV_SERIALIZE_OPT(get_txid, true) END_KV_SERIALIZE_MAP() @@ -471,16 +501,13 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<outkey> outs; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(outs) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -488,12 +515,13 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_GET_OUTPUTS { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<get_outputs_out> outputs; bool get_txid; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(outputs) KV_SERIALIZE(get_txid) END_KV_SERIALIZE_MAP() @@ -517,16 +545,13 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_access_response_base { std::vector<outkey> outs; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(outs) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -534,13 +559,14 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX { - struct request_t + struct request_t: public rpc_access_request_base { std::string tx_as_hex; bool do_not_relay; bool do_sanity_checks; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base); KV_SERIALIZE(tx_as_hex) KV_SERIALIZE_OPT(do_not_relay, false) KV_SERIALIZE_OPT(do_sanity_checks, true) @@ -549,9 +575,8 @@ namespace cryptonote typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::string reason; bool not_relayed; bool low_mixin; @@ -564,10 +589,9 @@ namespace cryptonote bool not_rct; bool too_few_outputs; bool sanity_check_failed; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(reason) KV_SERIALIZE(not_relayed) KV_SERIALIZE(low_mixin) @@ -580,7 +604,6 @@ namespace cryptonote KV_SERIALIZE(not_rct) KV_SERIALIZE(too_few_outputs) KV_SERIALIZE(sanity_check_failed) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -588,7 +611,7 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_START_MINING { - struct request_t + struct request_t: public rpc_request_base { std::string miner_address; uint64_t threads_count; @@ -596,6 +619,7 @@ namespace cryptonote bool ignore_battery; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(miner_address) KV_SERIALIZE(threads_count) KV_SERIALIZE(do_background_mining) @@ -604,12 +628,10 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -617,17 +639,16 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_GET_INFO { - struct request_t + struct request_t: public rpc_access_request_base { - BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base); END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; uint64_t height; uint64_t target_height; uint64_t difficulty; @@ -657,7 +678,6 @@ namespace cryptonote uint64_t start_time; uint64_t free_space; bool offline; - bool untrusted; std::string bootstrap_daemon_address; uint64_t height_without_bootstrap; bool was_bootstrap_ever_used; @@ -666,7 +686,7 @@ namespace cryptonote std::string version; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(height) KV_SERIALIZE(target_height) KV_SERIALIZE(difficulty) @@ -696,7 +716,6 @@ namespace cryptonote KV_SERIALIZE(start_time) KV_SERIALIZE(free_space) KV_SERIALIZE(offline) - KV_SERIALIZE(untrusted) KV_SERIALIZE(bootstrap_daemon_address) KV_SERIALIZE(height_without_bootstrap) KV_SERIALIZE(was_bootstrap_ever_used) @@ -712,18 +731,17 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_GET_NET_STATS { - struct request_t + struct request_t: public rpc_request_base { - BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; uint64_t start_time; uint64_t total_packets_in; uint64_t total_bytes_in; @@ -731,7 +749,7 @@ namespace cryptonote uint64_t total_bytes_out; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(start_time) KV_SERIALIZE(total_packets_in) KV_SERIALIZE(total_bytes_in) @@ -745,21 +763,19 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_STOP_MINING { - struct request_t + struct request_t: public rpc_request_base { - BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -768,18 +784,17 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_MINING_STATUS { - struct request_t + struct request_t: public rpc_request_base { - BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; bool active; uint64_t speed; uint32_t threads_count; @@ -797,7 +812,7 @@ namespace cryptonote uint64_t difficulty_top64; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(active) KV_SERIALIZE(speed) KV_SERIALIZE(threads_count) @@ -821,21 +836,19 @@ namespace cryptonote //----------------------------------------------- struct COMMAND_RPC_SAVE_BC { - struct request_t + struct request_t: public rpc_request_base { - BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -846,14 +859,13 @@ namespace cryptonote { typedef std::list<std::string> request; - struct response_t + struct response_t: public rpc_response_base { uint64_t count; - std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(count) - KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -869,7 +881,7 @@ namespace cryptonote struct COMMAND_RPC_GETBLOCKTEMPLATE { - struct request_t + struct request_t: public rpc_request_base { uint64_t reserve_size; //max 255 bytes std::string wallet_address; @@ -877,6 +889,7 @@ namespace cryptonote std::string extra_nonce; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(reserve_size) KV_SERIALIZE(wallet_address) KV_SERIALIZE(prev_block) @@ -885,7 +898,7 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { uint64_t difficulty; std::string wide_difficulty; @@ -894,14 +907,14 @@ namespace cryptonote uint64_t reserved_offset; uint64_t expected_reward; std::string prev_hash; + uint64_t seed_height; std::string seed_hash; std::string next_seed_hash; blobdata blocktemplate_blob; blobdata blockhashing_blob; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(difficulty) KV_SERIALIZE(wide_difficulty) KV_SERIALIZE(difficulty_top64) @@ -909,10 +922,9 @@ namespace cryptonote KV_SERIALIZE(reserved_offset) KV_SERIALIZE(expected_reward) KV_SERIALIZE(prev_hash) + KV_SERIALIZE(seed_height) KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(blockhashing_blob) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) KV_SERIALIZE(seed_hash) KV_SERIALIZE(next_seed_hash) END_KV_SERIALIZE_MAP() @@ -924,12 +936,10 @@ namespace cryptonote { typedef std::vector<std::string> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -937,7 +947,7 @@ namespace cryptonote struct COMMAND_RPC_GENERATEBLOCKS { - struct request_t + struct request_t: public rpc_request_base { uint64_t amount_of_blocks; std::string wallet_address; @@ -945,6 +955,7 @@ namespace cryptonote uint32_t starting_nonce; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(amount_of_blocks) KV_SERIALIZE(wallet_address) KV_SERIALIZE(prev_block) @@ -953,16 +964,15 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { uint64_t height; std::vector<std::string> blocks; - std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(height) KV_SERIALIZE(blocks) - KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1021,26 +1031,24 @@ namespace cryptonote struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { - struct request_t + struct request_t: public rpc_access_request_base { bool fill_pow_hash; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE_OPT(fill_pow_hash, false); END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; block_header_response block_header; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(block_header) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1049,13 +1057,14 @@ namespace cryptonote struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH { - struct request_t + struct request_t: public rpc_access_request_base { std::string hash; std::vector<std::string> hashes; bool fill_pow_hash; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(hash) KV_SERIALIZE(hashes) KV_SERIALIZE_OPT(fill_pow_hash, false); @@ -1063,18 +1072,15 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; block_header_response block_header; std::vector<block_header_response> block_headers; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(block_header) KV_SERIALIZE(block_headers) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1082,28 +1088,26 @@ namespace cryptonote struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT { - struct request_t + struct request_t: public rpc_access_request_base { uint64_t height; bool fill_pow_hash; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(height) KV_SERIALIZE_OPT(fill_pow_hash, false); END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; block_header_response block_header; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(block_header) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1111,13 +1115,14 @@ namespace cryptonote struct COMMAND_RPC_GET_BLOCK { - struct request_t + struct request_t: public rpc_access_request_base { std::string hash; uint64_t height; bool fill_pow_hash; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(hash) KV_SERIALIZE(height) KV_SERIALIZE_OPT(fill_pow_hash, false); @@ -1125,24 +1130,21 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; block_header_response block_header; std::string miner_tx_hash; std::vector<std::string> tx_hashes; std::string blob; std::string json; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(block_header) KV_SERIALIZE(miner_tx_hash) KV_SERIALIZE(tx_hashes) - KV_SERIALIZE(status) KV_SERIALIZE(blob) KV_SERIALIZE(json) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1154,19 +1156,20 @@ namespace cryptonote uint32_t ip; uint16_t port; uint16_t rpc_port; + uint32_t rpc_credits_per_hash; uint64_t last_seen; uint32_t pruning_seed; peer() = default; - peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) + peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash) + : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), rpc_credits_per_hash(rpc_credits_per_hash), last_seen(last_seen), pruning_seed(pruning_seed) {} - peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) + peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash) + : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), rpc_credits_per_hash(rpc_credits_per_hash), last_seen(last_seen), pruning_seed(pruning_seed) {} - peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) + peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash) + : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), rpc_credits_per_hash(rpc_credits_per_hash), last_seen(last_seen), pruning_seed(pruning_seed) {} BEGIN_KV_SERIALIZE_MAP() @@ -1175,6 +1178,7 @@ namespace cryptonote KV_SERIALIZE(ip) KV_SERIALIZE(port) KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) + KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0) KV_SERIALIZE(last_seen) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) END_KV_SERIALIZE_MAP() @@ -1182,24 +1186,24 @@ namespace cryptonote struct COMMAND_RPC_GET_PEER_LIST { - struct request_t + struct request_t: public rpc_request_base { bool public_only; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(public_only, true) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; std::vector<peer> white_list; std::vector<peer> gray_list; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(white_list) KV_SERIALIZE(gray_list) END_KV_SERIALIZE_MAP() @@ -1212,42 +1216,44 @@ namespace cryptonote std::string host; uint64_t last_seen; uint16_t rpc_port; + uint32_t rpc_credits_per_hash; - public_node() = delete; + public_node(): last_seen(0), rpc_port(0), rpc_credits_per_hash(0) {} public_node(const peer &peer) - : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port) + : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port), rpc_credits_per_hash(peer.rpc_credits_per_hash) {} BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(host) KV_SERIALIZE(last_seen) KV_SERIALIZE(rpc_port) + KV_SERIALIZE(rpc_credits_per_hash) END_KV_SERIALIZE_MAP() }; struct COMMAND_RPC_GET_PUBLIC_NODES { - struct request_t + struct request_t: public rpc_request_base { bool gray; bool white; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(gray, false) KV_SERIALIZE_OPT(white, true) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; std::vector<public_node> gray; std::vector<public_node> white; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(gray) KV_SERIALIZE(white) END_KV_SERIALIZE_MAP() @@ -1257,21 +1263,21 @@ namespace cryptonote struct COMMAND_RPC_SET_LOG_HASH_RATE { - struct request_t + struct request_t: public rpc_request_base { bool visible; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(visible) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1279,21 +1285,21 @@ namespace cryptonote struct COMMAND_RPC_SET_LOG_LEVEL { - struct request_t + struct request_t: public rpc_request_base { int8_t level; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(level) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1301,23 +1307,23 @@ namespace cryptonote struct COMMAND_RPC_SET_LOG_CATEGORIES { - struct request_t + struct request_t: public rpc_request_base { std::string categories; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(categories) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; std::string categories; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(categories) END_KV_SERIALIZE_MAP() }; @@ -1376,25 +1382,23 @@ namespace cryptonote struct COMMAND_RPC_GET_TRANSACTION_POOL { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<tx_info> transactions; std::vector<spent_key_image_info> spent_key_images; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(transactions) KV_SERIALIZE(spent_key_images) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1402,23 +1406,21 @@ namespace cryptonote struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<crypto::hash> tx_hashes; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1426,23 +1428,21 @@ namespace cryptonote struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<std::string> tx_hashes; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(tx_hashes) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1457,23 +1457,21 @@ namespace cryptonote struct COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<tx_backlog_entry> backlog; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1527,23 +1525,21 @@ namespace cryptonote struct COMMAND_RPC_GET_TRANSACTION_POOL_STATS { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; txpool_stats pool_stats; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(pool_stats) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1551,20 +1547,20 @@ namespace cryptonote struct COMMAND_RPC_GET_CONNECTIONS { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; std::list<connection_info> connections; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(connections) END_KV_SERIALIZE_MAP() }; @@ -1573,13 +1569,14 @@ namespace cryptonote struct COMMAND_RPC_GET_BLOCK_HEADERS_RANGE { - struct request_t + struct request_t: public rpc_access_request_base { uint64_t start_height; uint64_t end_height; bool fill_pow_hash; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(start_height) KV_SERIALIZE(end_height) KV_SERIALIZE_OPT(fill_pow_hash, false); @@ -1587,16 +1584,13 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<block_header_response> headers; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(headers) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1631,19 +1625,18 @@ namespace cryptonote struct COMMAND_RPC_STOP_DAEMON { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1651,19 +1644,18 @@ namespace cryptonote struct COMMAND_RPC_FAST_EXIT { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1671,25 +1663,23 @@ namespace cryptonote struct COMMAND_RPC_GET_LIMIT { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; uint64_t limit_up; uint64_t limit_down; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(limit_up) KV_SERIALIZE(limit_down) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1697,26 +1687,26 @@ namespace cryptonote struct COMMAND_RPC_SET_LIMIT { - struct request_t + struct request_t: public rpc_request_base { int64_t limit_down; // all limits (for get and set) are kB/s int64_t limit_up; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(limit_down) KV_SERIALIZE(limit_up) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; int64_t limit_up; int64_t limit_down; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(limit_up) KV_SERIALIZE(limit_down) END_KV_SERIALIZE_MAP() @@ -1726,25 +1716,26 @@ namespace cryptonote struct COMMAND_RPC_OUT_PEERS { - struct request_t + struct request_t: public rpc_request_base { bool set; uint32_t out_peers; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(set, true) KV_SERIALIZE(out_peers) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { uint32_t out_peers; - std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(out_peers) - KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1752,25 +1743,25 @@ namespace cryptonote struct COMMAND_RPC_IN_PEERS { - struct request_t + struct request_t: public rpc_request_base { bool set; uint32_t in_peers; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(set, true) KV_SERIALIZE(in_peers) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { uint32_t in_peers; - std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(in_peers) - KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1778,17 +1769,18 @@ namespace cryptonote struct COMMAND_RPC_HARD_FORK_INFO { - struct request_t + struct request_t: public rpc_access_request_base { uint8_t version; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(version) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { uint8_t version; bool enabled; @@ -1798,10 +1790,9 @@ namespace cryptonote uint8_t voting; uint32_t state; uint64_t earliest_height; - std::string status; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(version) KV_SERIALIZE(enabled) KV_SERIALIZE(window) @@ -1810,8 +1801,6 @@ namespace cryptonote KV_SERIALIZE(voting) KV_SERIALIZE(state) KV_SERIALIZE(earliest_height) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1832,20 +1821,20 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; std::vector<ban> bans; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(bans) END_KV_SERIALIZE_MAP() }; @@ -1869,22 +1858,21 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct request_t + struct request_t: public rpc_request_base { std::vector<ban> bans; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(bans) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1919,22 +1907,21 @@ namespace cryptonote struct COMMAND_RPC_FLUSH_TRANSACTION_POOL { - struct request_t + struct request_t: public rpc_request_base { std::vector<std::string> txids; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE(txids) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1942,7 +1929,7 @@ namespace cryptonote struct COMMAND_RPC_GET_OUTPUT_HISTOGRAM { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<uint64_t> amounts; uint64_t min_count; @@ -1951,6 +1938,7 @@ namespace cryptonote uint64_t recent_cutoff; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base); KV_SERIALIZE(amounts); KV_SERIALIZE(min_count); KV_SERIALIZE(max_count); @@ -1979,16 +1967,13 @@ namespace cryptonote entry() {} }; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<entry> histogram; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(histogram) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1996,25 +1981,23 @@ namespace cryptonote struct COMMAND_RPC_GET_VERSION { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; uint32_t version; bool release; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(version) KV_SERIALIZE(release) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2022,26 +2005,26 @@ namespace cryptonote struct COMMAND_RPC_GET_COINBASE_TX_SUM { - struct request_t + struct request_t: public rpc_access_request_base { uint64_t height; uint64_t count; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base); KV_SERIALIZE(height); KV_SERIALIZE(count); END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; uint64_t emission_amount; uint64_t fee_amount; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(emission_amount) KV_SERIALIZE(fee_amount) END_KV_SERIALIZE_MAP() @@ -2051,28 +2034,26 @@ namespace cryptonote struct COMMAND_RPC_GET_BASE_FEE_ESTIMATE { - struct request_t + struct request_t: public rpc_access_request_base { uint64_t grace_blocks; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(grace_blocks) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; uint64_t fee; uint64_t quantization_mask; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(fee) KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1) - KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2080,9 +2061,10 @@ namespace cryptonote struct COMMAND_RPC_GET_ALTERNATE_CHAINS { - struct request_t + struct request_t: public rpc_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -2110,13 +2092,12 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_response_base { - std::string status; std::vector<chain_info> chains; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(chains) END_KV_SERIALIZE_MAP() }; @@ -2125,21 +2106,21 @@ namespace cryptonote struct COMMAND_RPC_UPDATE { - struct request_t + struct request_t: public rpc_request_base { std::string command; std::string path; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(command); - KV_SERIALIZE(path); + KV_SERIALIZE_PARENT(rpc_request_base) + KV_SERIALIZE(command) + KV_SERIALIZE(path) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; bool update; std::string version; std::string user_uri; @@ -2148,7 +2129,7 @@ namespace cryptonote std::string path; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(update) KV_SERIALIZE(version) KV_SERIALIZE(user_uri) @@ -2162,22 +2143,21 @@ namespace cryptonote struct COMMAND_RPC_RELAY_TX { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<std::string> txids; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(txids) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2185,9 +2165,10 @@ namespace cryptonote struct COMMAND_RPC_SYNC_INFO { - struct request_t + struct request_t: public rpc_access_request_base { BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -2222,9 +2203,8 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; uint64_t height; uint64_t target_height; uint32_t next_needed_pruning_seed; @@ -2233,7 +2213,7 @@ namespace cryptonote std::string overview; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(height) KV_SERIALIZE(target_height) KV_SERIALIZE(next_needed_pruning_seed) @@ -2247,7 +2227,7 @@ namespace cryptonote struct COMMAND_RPC_GET_OUTPUT_DISTRIBUTION { - struct request_t + struct request_t: public rpc_access_request_base { std::vector<uint64_t> amounts; uint64_t from_height; @@ -2257,6 +2237,7 @@ namespace cryptonote bool compress; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) KV_SERIALIZE(amounts) KV_SERIALIZE_OPT(from_height, (uint64_t)0) KV_SERIALIZE_OPT(to_height, (uint64_t)0) @@ -2309,16 +2290,213 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_t + struct response_t: public rpc_access_response_base { - std::string status; std::vector<distribution> distributions; - bool untrusted; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(distributions) - KV_SERIALIZE(untrusted) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_ACCESS_INFO + { + struct request_t: public rpc_access_request_base + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t: public rpc_access_response_base + { + std::string hashing_blob; + uint64_t seed_height; + std::string seed_hash; + std::string next_seed_hash; + uint32_t cookie; + uint64_t diff; + uint64_t credits_per_hash_found; + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) + KV_SERIALIZE(hashing_blob) + KV_SERIALIZE(seed_height) + KV_SERIALIZE(seed_hash) + KV_SERIALIZE(next_seed_hash) + KV_SERIALIZE(cookie) + KV_SERIALIZE(diff) + KV_SERIALIZE(credits_per_hash_found) + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_ACCESS_SUBMIT_NONCE + { + struct request_t: public rpc_access_request_base + { + uint32_t nonce; + uint32_t cookie; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) + KV_SERIALIZE(nonce) + KV_SERIALIZE(cookie) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t: public rpc_access_response_base + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_ACCESS_PAY + { + struct request_t: public rpc_access_request_base + { + std::string paying_for; + uint64_t payment; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_request_base) + KV_SERIALIZE(paying_for) + KV_SERIALIZE(payment) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t: public rpc_access_response_base + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_access_response_base) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_ACCESS_TRACKING + { + struct request_t: public rpc_request_base + { + bool clear; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) + KV_SERIALIZE(clear) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct entry + { + std::string rpc; + uint64_t count; + uint64_t time; + uint64_t credits; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(rpc) + KV_SERIALIZE(count) + KV_SERIALIZE(time) + KV_SERIALIZE(credits) + END_KV_SERIALIZE_MAP() + }; + + struct response_t: public rpc_response_base + { + std::vector<entry> data; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) + KV_SERIALIZE(data) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_ACCESS_DATA + { + struct request_t: public rpc_request_base + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct entry + { + std::string client; + uint64_t balance; + uint64_t last_update_time; + uint64_t credits_total; + uint64_t credits_used; + uint64_t nonces_good; + uint64_t nonces_stale; + uint64_t nonces_bad; + uint64_t nonces_dupe; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(client) + KV_SERIALIZE(balance) + KV_SERIALIZE(last_update_time) + KV_SERIALIZE(credits_total) + KV_SERIALIZE(credits_used) + KV_SERIALIZE(nonces_good) + KV_SERIALIZE(nonces_stale) + KV_SERIALIZE(nonces_bad) + KV_SERIALIZE(nonces_dupe) + END_KV_SERIALIZE_MAP() + }; + + struct response_t: public rpc_response_base + { + std::list<entry> entries; + uint32_t hashrate; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) + KV_SERIALIZE(entries) + KV_SERIALIZE(hashrate) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + + struct COMMAND_RPC_ACCESS_ACCOUNT + { + struct request_t: public rpc_request_base + { + std::string client; + int64_t delta_balance; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) + KV_SERIALIZE(client) + KV_SERIALIZE_OPT(delta_balance, (int64_t)0) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t: public rpc_response_base + { + uint64_t credits; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_response_base) + KV_SERIALIZE(credits) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2326,23 +2504,23 @@ namespace cryptonote struct COMMAND_RPC_POP_BLOCKS { - struct request_t + struct request_t: public rpc_request_base { uint64_t nblocks; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(nblocks); + KV_SERIALIZE_PARENT(rpc_request_base) + KV_SERIALIZE(nblocks) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; uint64_t height; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(height) END_KV_SERIALIZE_MAP() }; @@ -2351,24 +2529,24 @@ namespace cryptonote struct COMMAND_RPC_PRUNE_BLOCKCHAIN { - struct request_t + struct request_t: public rpc_request_base { bool check; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(check, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; - struct response_t + struct response_t: public rpc_response_base { bool pruned; uint32_t pruning_seed; - std::string status; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) KV_SERIALIZE(pruned) KV_SERIALIZE(pruning_seed) END_KV_SERIALIZE_MAP() diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index b13049e61..2fd42f43f 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -43,5 +43,34 @@ #define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11 #define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12 #define CORE_RPC_ERROR_CODE_REGTEST_REQUIRED -13 +#define CORE_RPC_ERROR_CODE_PAYMENT_REQUIRED -14 +#define CORE_RPC_ERROR_CODE_INVALID_CLIENT -15 +#define CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW -16 +#define CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT -17 +#define CORE_RPC_ERROR_CODE_STALE_PAYMENT -18 +static inline const char *get_rpc_server_error_message(int64_t code) +{ + switch (code) + { + case CORE_RPC_ERROR_CODE_WRONG_PARAM: return "Invalid parameter"; + case CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT: return "Height is too large"; + case CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE: return "Reserve size is too large"; + case CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS: return "Wrong wallet address"; + case CORE_RPC_ERROR_CODE_INTERNAL_ERROR: return "Internal error"; + case CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB: return "Wrong block blob"; + case CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED: return "Block not accepted"; + case CORE_RPC_ERROR_CODE_CORE_BUSY: return "Core is busy"; + case CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE: return "Wrong block blob size"; + case CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC: return "Unsupported RPC"; + case CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS: return "Mining to subaddress is not supported"; + case CORE_RPC_ERROR_CODE_REGTEST_REQUIRED: return "Regtest mode required"; + case CORE_RPC_ERROR_CODE_PAYMENT_REQUIRED: return "Payment required"; + case CORE_RPC_ERROR_CODE_INVALID_CLIENT: return "Invalid client"; + case CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW: return "Payment too low"; + case CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT: return "Duplicate payment"; + case CORE_RPC_ERROR_CODE_STALE_PAYMENT: return "Stale payment"; + default: MERROR("Unknown error: " << code); return "Unknown error"; + } +} diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 2a43811cf..e64f5f163 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -80,6 +80,7 @@ namespace rpc uint32_t ip; uint16_t port; uint16_t rpc_port; + uint32_t rpc_credits_per_hash; uint64_t last_seen; uint32_t pruning_seed; }; diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp new file mode 100644 index 000000000..0637db728 --- /dev/null +++ b/src/rpc/rpc_payment.cpp @@ -0,0 +1,402 @@ +// Copyright (c) 2018-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <boost/archive/portable_binary_iarchive.hpp> +#include <boost/archive/portable_binary_oarchive.hpp> +#include "cryptonote_config.h" +#include "include_base_utils.h" +#include "string_tools.h" +#include "file_io_utils.h" +#include "int-util.h" +#include "common/util.h" +#include "serialization/crypto.h" +#include "common/unordered_containers_boost_serialization.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/difficulty.h" +#include "core_rpc_server_error_codes.h" +#include "rpc_payment.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc.payment" + +#define STALE_THRESHOLD 15 /* seconds */ + +#define PENALTY_FOR_STALE 0 +#define PENALTY_FOR_BAD_HASH 20 +#define PENALTY_FOR_DUPLICATE 20 + +#define DEFAULT_FLUSH_AGE (3600 * 24 * 180) // half a year +#define DEFAULT_ZERO_FLUSH_AGE (60 * 2) // 2 minutes + +#define RPC_PAYMENT_NONCE_TAIL 0x58 + +namespace cryptonote +{ + rpc_payment::client_info::client_info(): + cookie(0), + top(crypto::null_hash), + previous_top(crypto::null_hash), + credits(0), + update_time(time(NULL)), + last_request_timestamp(0), + block_template_update_time(0), + credits_total(0), + credits_used(0), + nonces_good(0), + nonces_stale(0), + nonces_bad(0), + nonces_dupe(0) + { + } + + rpc_payment::rpc_payment(const cryptonote::account_public_address &address, uint64_t diff, uint64_t credits_per_hash_found): + m_address(address), + m_diff(diff), + m_credits_per_hash_found(credits_per_hash_found), + m_credits_total(0), + m_credits_used(0), + m_nonces_good(0), + m_nonces_stale(0), + m_nonces_bad(0), + m_nonces_dupe(0) + { + } + + uint64_t rpc_payment::balance(const crypto::public_key &client, int64_t delta) + { + client_info &info = m_client_info[client]; // creates if not found + uint64_t credits = info.credits; + if (delta > 0 && credits > std::numeric_limits<uint64_t>::max() - delta) + credits = std::numeric_limits<uint64_t>::max(); + else if (delta < 0 && credits < (uint64_t)-delta) + credits = 0; + else + credits += delta; + if (delta) + MINFO("Client " << client << ": balance change from " << info.credits << " to " << credits); + return info.credits = credits; + } + + bool rpc_payment::pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits) + { + client_info &info = m_client_info[client]; // creates if not found + if (ts < info.last_request_timestamp || (ts == info.last_request_timestamp && !same_ts)) + { + MDEBUG("Invalid ts: " << ts << " <= " << info.last_request_timestamp); + return false; + } + info.last_request_timestamp = ts; + if (info.credits < payment) + { + MDEBUG("Not enough credits: " << info.credits << " < " << payment); + credits = info.credits; + return false; + } + info.credits -= payment; + add64clamp(&info.credits_used, payment); + add64clamp(&m_credits_used, payment); + MDEBUG("client " << client << " paying " << payment << " for " << rpc << ", " << info.credits << " left"); + credits = info.credits; + return true; + } + + bool rpc_payment::get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie) + { + client_info &info = m_client_info[client]; // creates if not found + const uint64_t now = time(NULL); + bool need_template = top != info.top || now >= info.block_template_update_time + STALE_THRESHOLD; + if (need_template) + { + cryptonote::block new_block; + uint64_t new_seed_height; + crypto::hash new_seed_hash; + cryptonote::blobdata extra_nonce("\x42\x42\x42\x42", 4); + if (!get_block_template(extra_nonce, new_block, new_seed_height, new_seed_hash)) + return false; + if(!remove_field_from_tx_extra(new_block.miner_tx.extra, typeid(cryptonote::tx_extra_nonce))) + return false; + char data[33]; + memcpy(data, &client, 32); + data[32] = RPC_PAYMENT_NONCE_TAIL; + crypto::hash hash; + cn_fast_hash(data, sizeof(data), hash); + extra_nonce = cryptonote::blobdata((const char*)&hash, 4); + if(!add_extra_nonce_to_tx_extra(new_block.miner_tx.extra, extra_nonce)) + return false; + info.previous_block = std::move(info.block); + info.block = std::move(new_block); + hashing_blob = get_block_hashing_blob(info.block); + info.previous_hashing_blob = info.hashing_blob; + info.hashing_blob = hashing_blob; + info.previous_top = info.top; + info.previous_seed_height = info.seed_height; + info.seed_height = new_seed_height; + info.previous_seed_hash = info.seed_hash; + info.seed_hash = new_seed_hash; + std::swap(info.previous_payments, info.payments); + info.payments.clear(); + ++info.cookie; + info.block_template_update_time = now; + } + info.top = top; + info.update_time = now; + hashing_blob = info.hashing_blob; + diff = m_diff; + credits_per_hash_found = m_credits_per_hash_found; + credits = info.credits; + seed_height = info.seed_height; + seed_hash = info.seed_hash; + cookie = info.cookie; + return true; + } + + bool rpc_payment::submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale) + { + client_info &info = m_client_info[client]; // creates if not found + if (cookie != info.cookie && cookie != info.cookie - 1) + { + MWARNING("Very stale nonce"); + ++m_nonces_stale; + ++info.nonces_stale; + sub64clamp(&info.credits, PENALTY_FOR_STALE * m_credits_per_hash_found); + error_code = CORE_RPC_ERROR_CODE_STALE_PAYMENT; + error_message = "Very stale payment"; + return false; + } + const bool is_current = cookie == info.cookie; + MINFO("client " << client << " sends nonce: " << nonce << ", " << (is_current ? "current" : "stale")); + std::unordered_set<uint64_t> &payments = is_current ? info.payments : info.previous_payments; + if (!payments.insert(nonce).second) + { + MWARNING("Duplicate nonce " << nonce << " from " << (is_current ? "current" : "previous")); + ++m_nonces_dupe; + ++info.nonces_dupe; + sub64clamp(&info.credits, PENALTY_FOR_DUPLICATE * m_credits_per_hash_found); + error_code = CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT; + error_message = "Duplicate payment"; + return false; + } + + const uint64_t now = time(NULL); + if (!is_current) + { + if (now > info.update_time + STALE_THRESHOLD) + { + MWARNING("Nonce is stale (top " << top << ", should be " << info.top << " or within " << STALE_THRESHOLD << " seconds"); + ++m_nonces_stale; + ++info.nonces_stale; + sub64clamp(&info.credits, PENALTY_FOR_STALE * m_credits_per_hash_found); + error_code = CORE_RPC_ERROR_CODE_STALE_PAYMENT; + error_message = "stale payment"; + return false; + } + } + + cryptonote::blobdata hashing_blob = is_current ? info.hashing_blob : info.previous_hashing_blob; + if (hashing_blob.size() < 43) + { + // not initialized ? + error_code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + error_message = "not initialized"; + return false; + } + + block = is_current ? info.block : info.previous_block; + *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(nonce); + if (block.major_version >= RX_BLOCK_VERSION) + { + const uint64_t seed_height = is_current ? info.seed_height : info.previous_seed_height; + const crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash; + const uint64_t height = cryptonote::get_block_height(block); + crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, 0, 0); + } + else + { + const int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0; + crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash, cn_variant, cryptonote::get_block_height(block)); + } + if (!check_hash(hash, m_diff)) + { + MWARNING("Payment too low"); + ++m_nonces_bad; + ++info.nonces_bad; + error_code = CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW; + error_message = "Hash does not meet difficulty (could be wrong PoW hash, or mining at lower difficulty than required, or attempt to defraud)"; + sub64clamp(&info.credits, PENALTY_FOR_BAD_HASH * m_credits_per_hash_found); + return false; + } + + add64clamp(&info.credits, m_credits_per_hash_found); + MINFO("client " << client << " credited for " << m_credits_per_hash_found << ", now " << info.credits << (is_current ? "" : " (close)")); + + m_hashrate[now] += m_diff; + add64clamp(&m_credits_total, m_credits_per_hash_found); + add64clamp(&info.credits_total, m_credits_per_hash_found); + ++m_nonces_good; + ++info.nonces_good; + + credits = info.credits; + block = info.block; + block.nonce = nonce; + stale = !is_current; + return true; + } + + bool rpc_payment::foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const + { + for (std::unordered_map<crypto::public_key, client_info>::const_iterator i = m_client_info.begin(); i != m_client_info.end(); ++i) + { + if (!f(i->first, i->second)) + return false; + } + return true; + } + + bool rpc_payment::load(std::string directory) + { + TRY_ENTRY(); + m_directory = std::move(directory); + std::string state_file_path = directory + "/" + RPC_PAYMENTS_DATA_FILENAME; + MINFO("loading rpc payments data from " << state_file_path); + std::ifstream data; + data.open(state_file_path, std::ios_base::binary | std::ios_base::in); + if (!data.fail()) + { + try + { + boost::archive::portable_binary_iarchive a(data); + a >> *this; + } + catch (const std::exception &e) + { + MERROR("Failed to load RPC payments file: " << e.what()); + m_client_info.clear(); + } + } + else + { + m_client_info.clear(); + } + + CATCH_ENTRY_L0("rpc_payment::load", false); + return true; + } + + bool rpc_payment::store(const std::string &directory_) const + { + TRY_ENTRY(); + const std::string &directory = directory_.empty() ? m_directory : directory_; + MDEBUG("storing rpc payments data to " << directory); + if (!tools::create_directories_if_necessary(directory)) + { + MWARNING("Failed to create data directory: " << directory); + return false; + } + const boost::filesystem::path state_file_path = (boost::filesystem::path(directory) / RPC_PAYMENTS_DATA_FILENAME); + if (boost::filesystem::exists(state_file_path)) + { + std::string state_file_path_old = state_file_path.string() + ".old"; + boost::system::error_code ec; + boost::filesystem::remove(state_file_path_old, ec); + std::error_code e = tools::replace_file(state_file_path.string(), state_file_path_old); + if (e) + MWARNING("Failed to rename " << state_file_path << " to " << state_file_path_old << ": " << e); + } + std::ofstream data; + data.open(state_file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (data.fail()) + { + MWARNING("Failed to save RPC payments to file " << state_file_path); + return false; + }; + boost::archive::portable_binary_oarchive a(data); + a << *this; + return true; + CATCH_ENTRY_L0("rpc_payment::store", false); + } + + unsigned int rpc_payment::flush_by_age(time_t seconds) + { + unsigned int count = 0; + const time_t now = time(NULL); + time_t seconds0 = seconds; + if (seconds == 0) + { + seconds = DEFAULT_FLUSH_AGE; + seconds0 = DEFAULT_ZERO_FLUSH_AGE; + } + const time_t threshold = seconds > now ? 0 : now - seconds; + const time_t threshold0 = seconds0 > now ? 0 : now - seconds0; + for (std::unordered_map<crypto::public_key, client_info>::iterator i = m_client_info.begin(); i != m_client_info.end(); ) + { + std::unordered_map<crypto::public_key, client_info>::iterator j = i++; + const time_t t = std::max(j->second.last_request_timestamp, j->second.update_time); + const bool erase = t < ((j->second.credits == 0) ? threshold0 : threshold); + if (erase) + { + MINFO("Erasing " << j->first << " with " << j->second.credits << " credits, inactive for " << (now-t)/86400 << " days"); + m_client_info.erase(j); + ++count; + } + } + return count; + } + + uint64_t rpc_payment::get_hashes(unsigned int seconds) const + { + const uint64_t now = time(NULL); + uint64_t hashes = 0; + for (std::map<uint64_t, uint64_t>::const_reverse_iterator i = m_hashrate.crbegin(); i != m_hashrate.crend(); ++i) + { + if (now > i->first + seconds) + break; + hashes += i->second; + } + return hashes; + } + + void rpc_payment::prune_hashrate(unsigned int seconds) + { + const uint64_t now = time(NULL); + std::map<uint64_t, uint64_t>::iterator i; + for (i = m_hashrate.begin(); i != m_hashrate.end(); ++i) + { + if (now <= i->first + seconds) + break; + } + m_hashrate.erase(m_hashrate.begin(), i); + } + + bool rpc_payment::on_idle() + { + flush_by_age(); + prune_hashrate(3600); + return true; + } +} diff --git a/src/rpc/rpc_payment.h b/src/rpc/rpc_payment.h new file mode 100644 index 000000000..f6832fd34 --- /dev/null +++ b/src/rpc/rpc_payment.h @@ -0,0 +1,146 @@ +// Copyright (c) 2018-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <string> +#include <unordered_set> +#include <unordered_map> +#include <boost/serialization/version.hpp> +#include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_basic/cryptonote_basic.h" + +namespace cryptonote +{ + class rpc_payment + { + public: + struct client_info + { + cryptonote::block block; + cryptonote::block previous_block; + cryptonote::blobdata hashing_blob; + cryptonote::blobdata previous_hashing_blob; + uint64_t previous_seed_height; + uint64_t seed_height; + crypto::hash previous_seed_hash; + crypto::hash seed_hash; + uint32_t cookie; + crypto::hash top; + crypto::hash previous_top; + uint64_t credits; + std::unordered_set<uint64_t> payments; + std::unordered_set<uint64_t> previous_payments; + uint64_t update_time; + uint64_t last_request_timestamp; + uint64_t block_template_update_time; + uint64_t credits_total; + uint64_t credits_used; + uint64_t nonces_good; + uint64_t nonces_stale; + uint64_t nonces_bad; + uint64_t nonces_dupe; + + client_info(); + + template <class t_archive> + inline void serialize(t_archive &a, const unsigned int ver) + { + a & block; + a & previous_block; + a & hashing_blob; + a & previous_hashing_blob; + a & seed_height; + a & previous_seed_height; + a & seed_hash; + a & previous_seed_hash; + a & cookie; + a & top; + a & previous_top; + a & credits; + a & payments; + a & previous_payments; + a & update_time; + a & last_request_timestamp; + a & block_template_update_time; + a & credits_total; + a & credits_used; + a & nonces_good; + a & nonces_stale; + a & nonces_bad; + a & nonces_dupe; + } + }; + + public: + rpc_payment(const cryptonote::account_public_address &address, uint64_t diff, uint64_t credits_per_hash_found); + uint64_t balance(const crypto::public_key &client, int64_t delta = 0); + bool pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits); + bool get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie); + bool submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale); + const cryptonote::account_public_address &get_payment_address() const { return m_address; } + bool foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const; + unsigned int flush_by_age(time_t seconds = 0); + uint64_t get_hashes(unsigned int seconds) const; + void prune_hashrate(unsigned int seconds); + bool on_idle(); + + template <class t_archive> + inline void serialize(t_archive &a, const unsigned int ver) + { + a & m_client_info; + a & m_hashrate; + a & m_credits_total; + a & m_credits_used; + a & m_nonces_good; + a & m_nonces_stale; + a & m_nonces_bad; + a & m_nonces_dupe; + } + + bool load(std::string directory); + bool store(const std::string &directory = std::string()) const; + + private: + cryptonote::account_public_address m_address; + uint64_t m_diff; + uint64_t m_credits_per_hash_found; + std::unordered_map<crypto::public_key, client_info> m_client_info; + std::string m_directory; + std::map<uint64_t, uint64_t> m_hashrate; + uint64_t m_credits_total; + uint64_t m_credits_used; + uint64_t m_nonces_good; + uint64_t m_nonces_stale; + uint64_t m_nonces_bad; + uint64_t m_nonces_dupe; + }; +} + +BOOST_CLASS_VERSION(cryptonote::rpc_payment, 0); +BOOST_CLASS_VERSION(cryptonote::rpc_payment::client_info, 0); diff --git a/src/rpc/rpc_payment_costs.h b/src/rpc/rpc_payment_costs.h new file mode 100644 index 000000000..3b27bf286 --- /dev/null +++ b/src/rpc/rpc_payment_costs.h @@ -0,0 +1,50 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#define COST_PER_BLOCK 0.05 +#define COST_PER_TX_RELAY 100 +#define COST_PER_OUT 1 +#define COST_PER_OUTPUT_INDEXES 1 +#define COST_PER_TX 0.5 +#define COST_PER_KEY_IMAGE 0.01 +#define COST_PER_POOL_HASH 0.01 +#define COST_PER_TX_POOL_STATS 0.2 +#define COST_PER_BLOCK_HEADER 0.1 +#define COST_PER_GET_INFO 1 +#define COST_PER_OUTPUT_HISTOGRAM 25000 +#define COST_PER_FULL_OUTPUT_HISTOGRAM 5000000 +#define COST_PER_OUTPUT_DISTRIBUTION_0 20 +#define COST_PER_OUTPUT_DISTRIBUTION 50000 +#define COST_PER_COINBASE_TX_SUM_BLOCK 2 +#define COST_PER_BLOCK_HASH 0.002 +#define COST_PER_FEE_ESTIMATE 1 +#define COST_PER_SYNC_INFO 2 +#define COST_PER_HARD_FORK_INFO 1 +#define COST_PER_PEER_LIST 2 diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp new file mode 100644 index 000000000..159bb5730 --- /dev/null +++ b/src/rpc/rpc_payment_signature.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2018-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <inttypes.h> +#include <stdlib.h> +#include <chrono> +#include "include_base_utils.h" +#include "string_tools.h" +#include "rpc_payment_signature.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc.payment" + +#define TIMESTAMP_LEEWAY (60 * 1000000) /* 60 seconds, in microseconds */ + +namespace cryptonote +{ + std::string make_rpc_payment_signature(const crypto::secret_key &skey) + { + std::string s; + crypto::public_key pkey; + crypto::secret_key_to_public_key(skey, pkey); + crypto::signature sig; + const uint64_t now = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + char ts[17]; + int ret = snprintf(ts, sizeof(ts), "%16.16" PRIx64, now); + CHECK_AND_ASSERT_MES(ret == 16, "", "snprintf failed"); + ts[16] = 0; + CHECK_AND_ASSERT_MES(strlen(ts) == 16, "", "Invalid time conversion"); + crypto::hash hash; + crypto::cn_fast_hash(ts, 16, hash); + crypto::generate_signature(hash, pkey, skey, sig); + s = epee::string_tools::pod_to_hex(pkey) + ts + epee::string_tools::pod_to_hex(sig); + return s; + } + + bool verify_rpc_payment_signature(const std::string &message, crypto::public_key &pkey, uint64_t &ts) + { + if (message.size() != 2 * sizeof(crypto::public_key) + 16 + 2 * sizeof(crypto::signature)) + { + MDEBUG("Bad message size: " << message.size()); + return false; + } + const std::string pkey_string = message.substr(0, 2 * sizeof(crypto::public_key)); + const std::string ts_string = message.substr(2 * sizeof(crypto::public_key), 16); + const std::string signature_string = message.substr(2 * sizeof(crypto::public_key) + 16); + if (!epee::string_tools::hex_to_pod(pkey_string, pkey)) + { + MDEBUG("Bad client id"); + return false; + } + crypto::signature signature; + if (!epee::string_tools::hex_to_pod(signature_string, signature)) + { + MDEBUG("Bad signature"); + return false; + } + crypto::hash hash; + crypto::cn_fast_hash(ts_string.data(), 16, hash); + if (!crypto::check_signature(hash, pkey, signature)) + { + MDEBUG("signature does not verify"); + return false; + } + char *endptr = NULL; + errno = 0; + unsigned long long ull = strtoull(ts_string.c_str(), &endptr, 16); + if (ull == ULLONG_MAX && errno == ERANGE) + { + MDEBUG("bad timestamp"); + return false; + } + ts = ull; + const uint64_t now = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); + if (ts > now + TIMESTAMP_LEEWAY) + { + MDEBUG("Timestamp is in the future"); + return false; + } + return true; + } +} diff --git a/src/rpc/rpc_payment_signature.h b/src/rpc/rpc_payment_signature.h new file mode 100644 index 000000000..4a2fe2ea3 --- /dev/null +++ b/src/rpc/rpc_payment_signature.h @@ -0,0 +1,39 @@ +// Copyright (c) 2018-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <stdint.h> +#include <string> +#include "crypto/crypto.h" + +namespace cryptonote +{ + std::string make_rpc_payment_signature(const crypto::secret_key &skey); + bool verify_rpc_payment_signature(const std::string &message, crypto::public_key &pkey, uint64_t &ts); +} |