rpc: remove pay-to-use RPC on node side

Co-authored-by: tobtoht <tob@featherwallet.org>
Co-authored-by: SNeedlewoods <sneedlewoods_1@protonmail.com>
This commit is contained in:
jeffro256
2023-05-05 12:12:52 -05:00
committed by tobtoht
parent 5e44d79824
commit 9b337ca01f
31 changed files with 12 additions and 2300 deletions
-2
View File
@@ -169,7 +169,6 @@
#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb"
#define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME "lock.mdb"
#define P2P_NET_DATA_FILENAME "p2pstate.bin"
#define RPC_PAYMENTS_DATA_FILENAME "rpcpayments.bin"
#define MINER_CONFIG_FILE_NAME "miner_conf.json"
#define THREAD_STACK_SIZE 5 * 1024 * 1024
@@ -251,7 +250,6 @@ namespace config
const unsigned char HASH_KEY_WALLET_CACHE = 0x8d;
const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e;
const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f;
const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58;
const unsigned char HASH_KEY_MEMORY = 'k';
const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char HASH_KEY_MULTISIG_KEY_AGGREGATION[] = "Multisig_key_agg";
-10
View File
@@ -954,16 +954,6 @@ bool t_command_parser_executor::pop_blocks(const std::vector<std::string>& args)
return true;
}
bool t_command_parser_executor::rpc_payments(const std::vector<std::string>& args)
{
if (args.size() != 0) {
std::cout << "Invalid syntax: No parameters expected. For more details, use the help command." << std::endl;
return true;
}
return m_executor.rpc_payments();
}
bool t_command_parser_executor::version(const std::vector<std::string>& args)
{
return m_executor.version();
-2
View File
@@ -142,8 +142,6 @@ public:
bool pop_blocks(const std::vector<std::string>& args);
bool rpc_payments(const std::vector<std::string>& args);
bool version(const std::vector<std::string>& args);
bool prune_blockchain(const std::vector<std::string>& args);
-5
View File
@@ -302,11 +302,6 @@ t_command_server::t_command_server(
, "pop_blocks <nblocks>"
, "Remove blocks from end of blockchain"
);
m_command_lookup.set_handler(
"rpc_payments"
, std::bind(&t_command_parser_executor::rpc_payments, &m_parser, p::_1)
, "Print information about RPC payments."
);
m_command_lookup.set_handler(
"version"
, std::bind(&t_command_parser_executor::version, &m_parser, p::_1)
+2 -2
View File
@@ -124,12 +124,12 @@ public:
const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
const auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
const bool has_restricted_rpc_port_arg = !command_line::is_arg_defaulted(vm, restricted_rpc_port_arg);
rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core", !has_restricted_rpc_port_arg});
rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"});
if(has_restricted_rpc_port_arg)
{
auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg);
rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted", true});
rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"});
}
if (!command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled))
+1 -2
View File
@@ -59,13 +59,12 @@ public:
, const bool restricted
, const std::string & port
, const std::string & description
, bool allow_rpc_payment
)
: m_server{core.get(), p2p.get()}, m_description{description}
{
MGINFO("Initializing " << m_description << " RPC server...");
if (!m_server.init(vm, restricted, port, allow_rpc_payment, command_line::get_arg(vm, daemon_args::arg_proxy)))
if (!m_server.init(vm, restricted, port, command_line::get_arg(vm, daemon_args::arg_proxy)))
{
throw std::runtime_error("Failed to initialize " + m_description + " RPC server.");
}
-42
View File
@@ -37,7 +37,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_basic/difficulty.h"
#include "cryptonote_basic/hardfork.h"
#include "rpc/rpc_payment_signature.h"
#include "rpc/rpc_version_str.h"
#include <boost/format.hpp>
#include <ctime>
@@ -2469,47 +2468,6 @@ bool t_rpc_command_executor::flush_cache(bool bad_blocks)
return true;
}
bool t_rpc_command_executor::rpc_payments()
{
cryptonote::COMMAND_RPC_ACCESS_DATA::request req;
cryptonote::COMMAND_RPC_ACCESS_DATA::response res;
std::string fail_message = "Unsuccessful";
epee::json_rpc::error error_resp;
if (m_is_rpc)
{
if (!m_rpc_client->json_rpc_request(req, res, "rpc_access_data", fail_message.c_str()))
{
return true;
}
}
else
{
if (!m_rpc_server->on_rpc_access_data(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
const uint64_t now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint64_t balance = 0;
tools::msg_writer() << boost::format("%64s %16u %16u %8u %8u %8u %8u %s")
% "Client ID" % "Balance" % "Total mined" % "Good" % "Stale" % "Bad" % "Dupes" % "Last update";
for (const auto &entry: res.entries)
{
tools::msg_writer() << boost::format("%64s %16u %16u %8u %8u %8u %8u %s")
% entry.client % entry.balance % entry.credits_total
% entry.nonces_good % entry.nonces_stale % entry.nonces_bad % entry.nonces_dupe
% (entry.last_update_time == 0 ? "never" : get_human_time_ago(entry.last_update_time, now).c_str());
balance += entry.balance;
}
tools::msg_writer() << res.entries.size() << " clients with a total of " << balance << " credits";
tools::msg_writer() << "Aggregated client hash rate: " << get_mining_speed(res.hashrate);
return true;
}
bool t_rpc_command_executor::version()
{
cryptonote::COMMAND_RPC_GET_INFO::request req;
-2
View File
@@ -170,8 +170,6 @@ public:
const std::string &password,
const std::string &proxy);
bool rpc_payments();
bool flush_cache(bool invalid_blocks);
};
-4
View File
@@ -28,14 +28,12 @@
set(rpc_base_sources
rpc_args.cpp
rpc_payment_signature.cpp
rpc_handler.cpp)
set(rpc_sources
bootstrap_daemon.cpp
bootstrap_node_selector.cpp
core_rpc_server.cpp
rpc_payment.cpp
rpc_version_str.cpp
instanciations.cpp)
@@ -54,7 +52,6 @@ set(daemon_rpc_server_sources
set(rpc_base_headers
rpc_args.h
rpc_payment_signature.h
rpc_handler.h)
set(rpc_headers
@@ -68,7 +65,6 @@ set(daemon_rpc_server_headers)
set(rpc_private_headers
bootstrap_daemon.h
core_rpc_server.h
rpc_payment.h
core_rpc_server_commands_defs.h
core_rpc_server_error_codes.h)
+1 -5
View File
@@ -17,10 +17,8 @@ namespace cryptonote
bootstrap_daemon::bootstrap_daemon(
std::function<std::map<std::string, bool>()> get_public_nodes,
bool rpc_payment_enabled,
const std::string &proxy)
: m_selector(new bootstrap_node::selector_auto(std::move(get_public_nodes)))
, m_rpc_payment_enabled(rpc_payment_enabled)
{
set_proxy(proxy);
}
@@ -28,10 +26,8 @@ namespace cryptonote
bootstrap_daemon::bootstrap_daemon(
const std::string &address,
boost::optional<epee::net_utils::http::login> credentials,
bool rpc_payment_enabled,
const std::string &proxy)
: m_selector(nullptr)
, m_rpc_payment_enabled(rpc_payment_enabled)
{
set_proxy(proxy);
if (!set_server(address, std::move(credentials)))
@@ -70,7 +66,7 @@ namespace cryptonote
bool bootstrap_daemon::handle_result(bool success, const std::string &status)
{
const bool failed = !success || (!m_rpc_payment_enabled && status == CORE_RPC_STATUS_PAYMENT_REQUIRED);
const bool failed = !success || (status == CORE_RPC_STATUS_PAYMENT_REQUIRED);
if (failed && m_selector)
{
const std::string current_address = address();
-3
View File
@@ -21,12 +21,10 @@ namespace cryptonote
public:
bootstrap_daemon(
std::function<std::map<std::string, bool>()> get_public_nodes,
bool rpc_payment_enabled,
const std::string &proxy);
bootstrap_daemon(
const std::string &address,
boost::optional<epee::net_utils::http::login> credentials,
bool rpc_payment_enabled,
const std::string &proxy);
std::string address() const noexcept;
@@ -82,7 +80,6 @@ namespace cryptonote
private:
net::http::client m_http_client;
const bool m_rpc_payment_enabled;
const std::unique_ptr<bootstrap_node::selector> m_selector;
boost::mutex m_selector_mutex;
};
+6 -446
View File
@@ -54,8 +54,6 @@ 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"
@@ -159,10 +157,6 @@ namespace cryptonote
command_line::add_arg(desc, arg_bootstrap_daemon_login);
command_line::add_arg(desc, arg_bootstrap_daemon_proxy);
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);
command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback);
command_line::add_arg(desc, arg_rpc_max_connections_per_public_ip);
command_line::add_arg(desc, arg_rpc_max_connections_per_private_ip);
command_line::add_arg(desc, arg_rpc_max_connections);
@@ -177,7 +171,6 @@ namespace cryptonote
, m_p2p(p2p)
, m_was_bootstrap_ever_used(false)
, disable_rpc_ban(false)
, m_rpc_payment_allow_free_loopback(false)
{}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::set_bootstrap_daemon(
@@ -194,7 +187,7 @@ namespace cryptonote
return set_bootstrap_daemon(address, credentials, proxy);
}
//------------------------------------------------------------------------------------------------------------------------------
std::map<std::string, bool> core_rpc_server::get_public_nodes(uint32_t credits_per_hash_threshold/* = 0*/)
std::map<std::string, bool> core_rpc_server::get_public_nodes(uint32_t/* = 0*/)
{
COMMAND_RPC_GET_PUBLIC_NODES::request request;
COMMAND_RPC_GET_PUBLIC_NODES::response response;
@@ -209,16 +202,10 @@ namespace cryptonote
std::map<std::string, bool> result;
const auto append = [&result, &credits_per_hash_threshold](const std::vector<public_node> &nodes, bool white) {
const auto append = [&result](const std::vector<public_node> &nodes, bool white) {
for (const auto &node : nodes)
{
const bool rpc_payment_enabled = credits_per_hash_threshold > 0;
const bool node_rpc_payment_enabled = node.rpc_credits_per_hash > 0;
if (!node_rpc_payment_enabled ||
(rpc_payment_enabled && node.rpc_credits_per_hash >= credits_per_hash_threshold))
{
result.insert(std::make_pair(node.host + ":" + std::to_string(node.rpc_port), white));
}
result.insert(std::make_pair(node.host + ":" + std::to_string(node.rpc_port), white));
}
};
@@ -235,9 +222,6 @@ namespace cryptonote
{
boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
constexpr const uint32_t credits_per_hash_threshold = 0;
constexpr const bool rpc_payment_enabled = credits_per_hash_threshold != 0;
if (address.empty())
{
m_bootstrap_daemon.reset(nullptr);
@@ -245,13 +229,13 @@ namespace cryptonote
else if (address == "auto")
{
auto get_nodes = [this]() {
return get_public_nodes(credits_per_hash_threshold);
return get_public_nodes(0);
};
m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled, m_bootstrap_daemon_proxy.empty() ? proxy : m_bootstrap_daemon_proxy));
m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), m_bootstrap_daemon_proxy.empty() ? proxy : m_bootstrap_daemon_proxy));
}
else
{
m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled, m_bootstrap_daemon_proxy.empty() ? proxy : m_bootstrap_daemon_proxy));
m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, m_bootstrap_daemon_proxy.empty() ? proxy : m_bootstrap_daemon_proxy));
}
m_should_use_bootstrap_daemon = m_bootstrap_daemon.get() != nullptr;
@@ -260,15 +244,12 @@ namespace cryptonote
}
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
, const bool restricted
, const std::string& port
, bool allow_rpc_payment
, const std::string& proxy
)
{
@@ -295,45 +276,6 @@ namespace cryptonote
}
disable_rpc_ban = rpc_config->disable_rpc_ban;
const std::string data_dir{command_line::get_arg(vm, cryptonote::arg_data_dir)};
std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
if (!address.empty() && allow_rpc_payment)
{
if (!m_restricted && nettype() != FAKECHAIN)
{
MFATAL("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))
{
MFATAL("Invalid payment address: " << address);
return false;
}
if (info.is_subaddress)
{
MFATAL("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)
{
MFATAL("Payments difficulty and/or payments credits are 0, but a payment address was given");
return false;
}
m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback);
m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
m_rpc_payment->load(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, bind_ip_str);
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),
@@ -349,9 +291,6 @@ 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(); }, std::chrono::minutes{1});
bool store_ssl_key = !restricted && rpc_config->ssl_options && rpc_config->ssl_options.auth.certificate_path.empty();
const auto ssl_base_path = (boost::filesystem::path{data_dir} / "rpc_ssl").string();
const bool ssl_cert_file_exists = boost::filesystem::exists(ssl_base_path + ".crt");
@@ -436,44 +375,6 @@ namespace cryptonote
}
return inited;
}
//------------------------------------------------------------------------------------------------------------------------------
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;
}
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 || (m_rpc_payment_allow_free_loopback && ctx->m_remote_address.is_loopback())) 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()
{
@@ -539,8 +440,6 @@ namespace cryptonote
return r;
}
CHECK_PAYMENT_MIN1(req, res, COST_PER_GET_INFO, false);
const bool restricted = m_restricted && ctx;
crypto::hash top_hash;
@@ -644,8 +543,6 @@ namespace cryptonote
return use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_FAST>(invoke_http_mode::BIN, "/getblocks.bin", req, res, r);
}
CHECK_PAYMENT(req, res, 1);
res.daemon_time = (uint64_t)time(NULL);
// Always set daemon time, and set it early rather than late, as delivering some incremental pool
// info twice because of slightly overlapping time intervals is no problem, whereas producing gaps
@@ -698,16 +595,6 @@ namespace cryptonote
? std::min(req.max_block_count, (uint64_t)COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT)
: COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
if (m_rpc_payment)
{
max_blocks = std::min((size_t)(res.credits / COST_PER_BLOCK), max_blocks);
if (max_blocks == 0)
{
res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
return true;
}
}
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.top_block_hash, res.start_height, req.prune, !req.no_miner_tx, req.block_ids_exclusive, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
{
@@ -716,8 +603,6 @@ namespace cryptonote
return true;
}
CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
size_t ntxes = 0;
res.blocks.reserve(bs.size());
res.output_indices.reserve(bs.size());
@@ -797,12 +682,6 @@ namespace cryptonote
}
res.added_pool_txs.clear();
if (m_rpc_payment)
{
CHECK_PAYMENT_SAME_TS(req, res,
added_pool_txs.size() * COST_PER_TX +
(res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH);
}
for (const auto &added_pool_tx: added_pool_txs)
{
COMMAND_RPC_GET_BLOCKS_FAST::pool_tx_info info;
@@ -875,7 +754,6 @@ 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;
@@ -907,8 +785,6 @@ namespace cryptonote
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))
{
@@ -917,8 +793,6 @@ namespace cryptonote
return true;
}
CHECK_PAYMENT_SAME_TS(req, res, res.m_block_ids.size() * COST_PER_BLOCK_HASH);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -930,8 +804,6 @@ namespace cryptonote
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;
@@ -960,8 +832,6 @@ namespace cryptonote
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;
@@ -1007,8 +877,6 @@ namespace cryptonote
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)
{
@@ -1036,8 +904,6 @@ namespace cryptonote
return true;
}
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)
{
@@ -1284,8 +1150,6 @@ namespace cryptonote
return true;
}
CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false);
// parse key images from request
std::vector<crypto::key_image> key_images;
for(const auto& ki_hex_str: req.key_images)
@@ -1387,8 +1251,6 @@ namespace cryptonote
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))
{
@@ -1721,8 +1583,6 @@ namespace cryptonote
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;
const bool allow_sensitive = !request_has_rpc_origin || !restricted;
@@ -1730,7 +1590,6 @@ namespace cryptonote
size_t n_txes = m_core.get_pool_transactions_count(allow_sensitive);
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, allow_sensitive);
for (tx_info& txi : res.transactions)
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
@@ -1747,8 +1606,6 @@ namespace cryptonote
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;
const bool allow_sensitive = !request_has_rpc_origin || !restricted;
@@ -1756,7 +1613,6 @@ namespace cryptonote
size_t n_txes = m_core.get_pool_transactions_count(allow_sensitive);
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, allow_sensitive);
}
@@ -1771,8 +1627,6 @@ namespace cryptonote
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;
const bool allow_sensitive = !request_has_rpc_origin || !restricted;
@@ -1780,7 +1634,6 @@ namespace cryptonote
size_t n_txes = m_core.get_pool_transactions_count(allow_sensitive);
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, allow_sensitive);
res.tx_hashes.reserve(tx_hashes.size());
@@ -1799,8 +1652,6 @@ namespace cryptonote
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);
@@ -2561,7 +2412,6 @@ namespace cryptonote
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);
@@ -2592,8 +2442,6 @@ namespace cryptonote
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);
const bool restricted = m_restricted && ctx;
if (restricted && req.hashes.size() > RESTRICTED_BLOCK_COUNT)
{
@@ -2676,7 +2524,6 @@ namespace cryptonote
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);
@@ -2727,7 +2574,6 @@ 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);
@@ -2756,8 +2602,6 @@ namespace cryptonote
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())
{
@@ -2843,7 +2687,6 @@ namespace cryptonote
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();
@@ -3040,9 +2883,6 @@ namespace cryptonote
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";
@@ -3098,7 +2938,6 @@ namespace cryptonote
res.status = "requested range exceeds blockchain height";
return true;
}
CHECK_PAYMENT_MIN1(req, res, COST_PER_COINBASE_TX_SUM_BLOCK * req.count, false);
std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count);
store_128(amounts.first, res.emission_amount, res.wide_emission_amount, res.emission_amount_top64);
store_128(amounts.second, res.fee_amount, res.wide_fee_amount, res.fee_amount_top64);
@@ -3113,8 +2952,6 @@ namespace cryptonote
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);
{
m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees);
res.fee = res.fees[0];
@@ -3348,7 +3185,6 @@ 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)
{
RPC_TRACKER(relay_tx);
CHECK_PAYMENT_MIN1(req, res, req.txids.size() * COST_PER_TX_RELAY, false);
const bool restricted = m_restricted && ctx;
@@ -3400,7 +3236,6 @@ 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)
{
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);
@@ -3429,8 +3264,6 @@ namespace cryptonote
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))
{
@@ -3458,11 +3291,6 @@ namespace cryptonote
return false;
}
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
@@ -3506,11 +3334,6 @@ namespace cryptonote
return false;
}
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)
@@ -3569,70 +3392,6 @@ namespace cryptonote
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, "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, cumulative_weight;
size_t reserved_offset;
if (!get_block_template(m_rpc_payment->get_payment_address(), NULL, extra_nonce, reserved_offset, difficulty, height, expected_reward, cumulative_weight, 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_flush_cache(const COMMAND_RPC_FLUSH_CACHE::request& req, COMMAND_RPC_FLUSH_CACHE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
RPC_TRACKER(flush_cache);
@@ -3718,181 +3477,6 @@ namespace cryptonote
#endif
}
//------------------------------------------------------------------------------------------------------------------------------
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, "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, "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, "rpc_access_data", req, res, r))
return r;
if (!m_rpc_payment)
{
error_resp.code = CORE_RPC_ERROR_CODE_PAYMENTS_NOT_ENABLED;
error_resp.message = "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, "rpc_access_account", req, res, r))
return r;
if (!m_rpc_payment)
{
error_resp.code = CORE_RPC_ERROR_CODE_PAYMENTS_NOT_ENABLED;
error_resp.message = "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"
@@ -3938,30 +3522,6 @@ namespace cryptonote
, ""
};
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
};
const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_payment_allow_free_loopback = {
"rpc-payment-allow-free-loopback"
, "Allow free access from the loopback address (ie, the local host)"
, false
};
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_max_connections_per_public_ip = {
"rpc-max-connections-per-public-ip"
, "Max RPC connections per public IP permitted"
-21
View File
@@ -42,7 +42,6 @@
#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"
@@ -68,10 +67,6 @@ namespace cryptonote
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_bootstrap_daemon_proxy;
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;
static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback;
static const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_public_ip;
static const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections_per_private_ip;
static const command_line::arg_descriptor<std::size_t> arg_rpc_max_connections;
@@ -90,7 +85,6 @@ namespace cryptonote
const boost::program_options::variables_map& vm,
const bool restricted,
const std::string& port,
bool allow_rpc_payment,
const std::string& proxy = {}
);
network_type nettype() const { return m_core.get_nettype(); }
@@ -182,12 +176,6 @@ namespace cryptonote
MAP_JON_RPC_WE_IF("prune_blockchain", on_prune_blockchain, COMMAND_RPC_PRUNE_BLOCKCHAIN, !m_restricted)
MAP_JON_RPC_WE_IF("flush_cache", on_flush_cache, COMMAND_RPC_FLUSH_CACHE, !m_restricted)
MAP_JON_RPC_WE("get_txids_loose", on_get_txids_loose, COMMAND_RPC_GET_TXIDS_LOOSE)
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()
@@ -260,12 +248,6 @@ namespace cryptonote
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_flush_cache(const COMMAND_RPC_FLUSH_CACHE::request& req, COMMAND_RPC_FLUSH_CACHE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_txids_loose(const COMMAND_RPC_GET_TXIDS_LOOSE::request& req, COMMAND_RPC_GET_TXIDS_LOOSE::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:
@@ -289,7 +271,6 @@ private:
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, uint64_t& cumulative_weight, 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;
@@ -302,9 +283,7 @@ private:
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;
bool disable_rpc_ban;
bool m_rpc_payment_allow_free_loopback;
};
}
-200
View File
@@ -2493,206 +2493,6 @@ inline const std::string get_rpc_status(const bool trusted_daemon, const std::st
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;
};
struct COMMAND_RPC_POP_BLOCKS
{
struct request_t: public rpc_request_base
-429
View File
@@ -1,429 +0,0 @@
// Copyright (c) 2018-2024, 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/filesystem.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 "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
namespace cryptonote
{
rpc_payment::client_info::client_info():
previous_seed_height(0),
seed_height(0),
previous_seed_hash(crypto::null_hash),
seed_hash(crypto::null_hash),
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)
{
boost::lock_guard<boost::mutex> lock(mutex);
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)
{
boost::lock_guard<boost::mutex> lock(mutex);
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)
{
boost::lock_guard<boost::mutex> lock(mutex);
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] = config::HASH_KEY_RPC_PAYMENT_NONCE;
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 = 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)
{
boost::lock_guard<boost::mutex> lock(mutex);
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 crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash;
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data);
}
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
{
boost::lock_guard<boost::mutex> lock(mutex);
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();
boost::lock_guard<boost::mutex> lock(mutex);
m_directory = std::move(directory);
std::string state_file_path = m_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);
std::string bytes(std::istream_iterator<char>{data}, std::istream_iterator<char>{});
if (!data.fail())
{
bool loaded = false;
try
{
binary_archive<false> ar{epee::strspan<std::uint8_t>(bytes)};
if (::serialization::serialize(ar, *this))
if (::serialization::check_stream_state(ar))
loaded = true;
}
catch (...) {}
if (!loaded)
{
bytes.clear();
bytes.shrink_to_fit();
try
{
boost::archive::portable_binary_iarchive a(data);
a >> *this;
loaded = true;
}
catch (...) {}
}
if (!loaded)
{
MERROR("Failed to load RPC payments file");
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();
boost::lock_guard<boost::mutex> lock(mutex);
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;
};
binary_archive<true> ar(data);
if (!::serialization::serialize(ar, *const_cast<rpc_payment*>(this)))
return false;
return true;
CATCH_ENTRY_L0("rpc_payment::store", false);
}
unsigned int rpc_payment::flush_by_age(time_t seconds)
{
boost::lock_guard<boost::mutex> lock(mutex);
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 / 1000000, 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
{
boost::lock_guard<boost::mutex> lock(mutex);
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)
{
boost::lock_guard<boost::mutex> lock(mutex);
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;
}
}
-191
View File
@@ -1,191 +0,0 @@
// Copyright (c) 2018-2024, 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 <map>
#include <boost/thread/mutex.hpp>
#include <boost/serialization/version.hpp>
#include "cryptonote_basic/blobdatatype.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include "serialization/crypto.h"
#include "serialization/string.h"
#include "serialization/pair.h"
#include "serialization/containers.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;
}
BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
FIELD(block)
FIELD(previous_block)
FIELD(hashing_blob)
FIELD(previous_hashing_blob)
VARINT_FIELD(seed_height)
VARINT_FIELD(previous_seed_height)
FIELD(seed_hash)
FIELD(previous_seed_hash)
VARINT_FIELD(cookie)
FIELD(top)
FIELD(previous_top)
VARINT_FIELD(credits)
FIELD(payments)
FIELD(previous_payments)
FIELD(update_time)
FIELD(last_request_timestamp)
FIELD(block_template_update_time)
VARINT_FIELD(credits_total)
VARINT_FIELD(credits_used)
VARINT_FIELD(nonces_good)
VARINT_FIELD(nonces_stale)
VARINT_FIELD(nonces_bad)
VARINT_FIELD(nonces_dupe)
END_SERIALIZE()
};
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;
}
BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
FIELD(m_client_info)
FIELD(m_hashrate)
VARINT_FIELD(m_credits_total)
VARINT_FIELD(m_credits_used)
VARINT_FIELD(m_nonces_good)
VARINT_FIELD(m_nonces_stale)
VARINT_FIELD(m_nonces_bad)
VARINT_FIELD(m_nonces_dupe)
END_SERIALIZE()
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;
mutable boost::mutex mutex;
};
}
-50
View File
@@ -1,50 +0,0 @@
// Copyright (c) 2019-2024, 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
-112
View File
@@ -1,112 +0,0 @@
// Copyright (c) 2018-2024, 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 <cinttypes>
#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;
}
if (ts < now - TIMESTAMP_LEEWAY)
{
MDEBUG("Timestamp is too old");
return false;
}
return true;
}
}
-39
View File
@@ -1,39 +0,0 @@
// Copyright (c) 2018-2024, 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);
}
-1
View File
@@ -1717,7 +1717,6 @@ private:
bool m_show_wallet_name_when_locked;
uint32_t m_inactivity_lock_timeout;
BackgroundMiningSetupType m_setup_background_mining;
float m_auto_mine_for_rpc_payment_threshold;
bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
-14
View File
@@ -50,20 +50,6 @@ target_link_libraries(functional_tests
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set(make_test_signature_sources
make_test_signature.cc)
monero_add_minimal_executable(make_test_signature
${make_test_signature_sources})
target_link_libraries(make_test_signature
PRIVATE
rpc_base
cncrypto
epee
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
monero_add_minimal_executable(cpu_power_test cpu_power_test.cpp)
find_package(Python3 REQUIRED)
@@ -13,7 +13,7 @@ USAGE = 'usage: functional_tests_rpc.py <python> <srcdir> <builddir> [<tests-to-
DEFAULT_TESTS = [
'address_book', 'bans', 'blockchain', 'cold_signing', 'daemon_info', 'get_output_distribution',
'http_digest_auth', 'integrated_address', 'k_anonymity', 'mining', 'multisig', 'p2p', 'proofs',
'rpc_payment', 'sign_message', 'transfer', 'txpool', 'uri', 'validate_address', 'wallet'
'sign_message', 'transfer', 'txpool', 'uri', 'validate_address', 'wallet'
]
try:
python = sys.argv[1]
@@ -39,7 +39,6 @@ except:
tests = DEFAULT_TESTS
# a main offline monerod, does most of the tests
# a restricted RPC monerod setup with RPC payment
# two local online monerods connected to each other
N_MONERODS = 5
@@ -56,7 +55,7 @@ monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", st
monerod_extra = [
["--offline"],
["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"],
["--offline"],
["--add-exclusive-node", "127.0.0.1:18283"],
["--add-exclusive-node", "127.0.0.1:18282"],
["--rpc-login", "md5_lover:Z1ON0101", "--offline"],
@@ -1,79 +0,0 @@
// Copyright (c) 2019-2024, 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 <stdio.h>
#include "misc_language.h"
#include "misc_log_ex.h"
#include "string_tools.h"
#include "rpc/rpc_payment_signature.h"
int main(int argc, const char **argv)
{
TRY_ENTRY();
if (argc > 3)
{
fprintf(stderr, "usage: %s [<secret_key> [N]]\n", argv[0]);
return 1;
}
crypto::secret_key skey;
if (argc == 1)
{
crypto::public_key pkey;
crypto::random32_unbiased((unsigned char*)skey.data);
crypto::secret_key_to_public_key(skey, pkey);
printf("%s %s\n", epee::to_hex::string({to_bytes(skey), 32}).c_str(), epee::string_tools::pod_to_hex(pkey).c_str());
return 0;
}
if (!epee::string_tools::hex_to_pod(argv[1], skey))
{
fprintf(stderr, "invalid secret key\n");
return 1;
}
uint32_t count = 1;
if (argc == 3)
{
int i = atoi(argv[2]);
if (i <= 0)
{
fprintf(stderr, "invalid count\n");
return 1;
}
count = (uint32_t)i;
}
while (count--)
{
std::string signature = cryptonote::make_rpc_payment_signature(skey);
epee::misc_utils::sleep_no_w(1);
printf("%s\n", signature.c_str());
}
return 0;
CATCH_ENTRY_L0("main()", 1);
}
-452
View File
@@ -1,452 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2019-2024, 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.
import subprocess
import os
import time
"""Test daemon RPC payment calls
"""
from framework.daemon import Daemon
from framework.wallet import Wallet
class RPCPaymentTest():
def run_test(self):
self.make_test_signature = os.environ['MAKE_TEST_SIGNATURE']
assert len(self.make_test_signature) > 0
self.secret_key, self.public_key = self.get_keys()
self.signatures = []
self.reset()
self.test_access_tracking()
self.test_access_mining()
self.test_access_payment()
self.test_access_account()
self.test_free_access()
def get_keys(self):
output = subprocess.check_output([self.make_test_signature]).decode('utf-8').rstrip()
fields = output.split()
assert len(fields) == 2
return fields
def refill_signatures(self):
self.signatures_time = time.time()
self.signatures = []
signatures = subprocess.check_output([self.make_test_signature, self.secret_key, '256']).decode('utf-8')
for line in signatures.split():
self.signatures.append(line.rstrip())
def get_signature(self):
if len(self.signatures) == 0 or self.signatures_time + 10 < time.time():
self.refill_signatures()
s = self.signatures[0]
self.signatures = self.signatures[1:]
return s
def reset(self):
print('Resetting blockchain')
daemon = Daemon(idx=1)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def test_access_tracking(self):
print('Testing access tracking')
daemon = Daemon(idx=1)
res = daemon.rpc_access_tracking(True)
res = daemon.rpc_access_tracking()
data = sorted(res.data, key = lambda k: k['rpc'])
assert len(data) == 1
entry = data[0]
assert entry.rpc == 'rpc_access_tracking'
assert entry.count == 1
assert entry.time >= 0
assert entry.credits == 0
daemon.get_connections()
res = daemon.rpc_access_tracking()
data = sorted(res.data, key = lambda k: k['rpc'])
assert len(data) == 2
entry = data[0]
assert entry.rpc == 'get_connections'
assert entry.count == 1
assert entry.time >= 0
assert entry.credits == 0
daemon.get_connections()
res = daemon.rpc_access_tracking()
data = sorted(res.data, key = lambda k: k['rpc'])
assert len(data) == 2
entry = data[0]
assert entry.rpc == 'get_connections'
assert entry.count == 2
assert entry.time >= 0
assert entry.credits == 0
daemon.get_alternate_chains()
res = daemon.rpc_access_tracking()
data = sorted(res.data, key = lambda k: k['rpc'])
assert len(data) == 3
entry = data[0]
assert entry.rpc == 'get_alternate_chains'
assert entry.count == 1
assert entry.time >= 0
assert entry.credits == 0
entry = res.data[1]
assert entry.rpc == 'get_connections'
assert entry.count == 2
assert entry.time >= 0
assert entry.credits == 0
res = daemon.rpc_access_tracking(True)
res = daemon.rpc_access_tracking()
data = sorted(res.data, key = lambda k: k['rpc'])
assert len(data) == 1
entry = data[0]
assert entry.rpc == 'rpc_access_tracking'
assert entry.count == 1
def test_access_mining(self):
print('Testing access mining')
daemon = Daemon(idx=1)
wallet = Wallet(idx=3)
res = daemon.rpc_access_info(client = self.get_signature())
assert len(res.hashing_blob) > 39
assert res.height == 1
assert res.top_hash == '418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3'
assert res.credits_per_hash_found == 5000
assert res.diff == 10
assert res.credits == 0
cookie = res.cookie
# Try random nonces till we find one that's valid and one that's invalid
nonce = 0
found_valid = 0
found_invalid = 0
last_credits = 0
loop_time = time.time()
while found_valid == 0 or found_invalid == 0:
nonce += 1
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature())
found_valid += 1
assert res.credits == last_credits + 5000
except Exception as e:
found_invalid += 1
res = daemon.rpc_access_info(client = self.get_signature())
cookie = res.cookie
loop_time = time.time()
assert res.credits < last_credits or res.credits == 0
assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails
last_credits = res.credits
if time.time() >= loop_time + 10:
res = daemon.rpc_access_info(client = self.get_signature())
cookie = res.cookie
loop_time = time.time()
# we should now have 1 valid nonce, and a number of bad ones
res = daemon.rpc_access_info(client = self.get_signature())
assert len(res.hashing_blob) > 39
assert res.height > 1
assert res.top_hash != '418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3' # here, any share matches network diff
assert res.credits_per_hash_found == 5000
assert res.diff == 10
cookie = res.cookie
res = daemon.rpc_access_data()
assert len(res.entries) > 0
e = [x for x in res.entries if x['client'] == self.public_key]
assert len(e) == 1
e = e[0]
assert e.nonces_stale == 0
assert e.nonces_bad == found_invalid
assert e.nonces_good == found_valid
assert e.nonces_dupe == 0
# Try random nonces till we find one that's valid so we get a load of credits
loop_time = time.time()
while last_credits == 0:
nonce += 1
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature())
found_valid += 1
last_credits = res.credits
break
except:
found_invalid += 1
assert nonce < 1000 # can't find a valid none -> the RPC probably fails
if time.time() >= loop_time + 10:
res = daemon.rpc_access_info(client = self.get_signature())
cookie = res.cookie
loop_time = time.time()
# we should now have at least 5000
res = daemon.rpc_access_info(client = self.get_signature())
assert res.credits == last_credits
assert res.credits >= 5000 # last one was a valid nonce
res = daemon.rpc_access_data()
assert len(res.entries) > 0
e = [x for x in res.entries if x['client'] == self.public_key]
assert len(e) == 1
e = e[0]
assert e.nonces_stale == 0
assert e.nonces_bad == found_invalid
assert e.nonces_good == found_valid
assert e.nonces_dupe == 0
assert e.balance == 5000
assert e.credits_total >= 5000
# find a valid one, then check dupes aren't allowed
res = daemon.rpc_access_info(client = self.get_signature())
cookie = res.cookie
old_cookie = cookie # we keep that so can submit a stale later
loop_time = time.time()
while True:
nonce += 1
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature())
found_valid += 1
break
except:
found_invalid += 1
assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails
if time.time() >= loop_time + 10:
res = daemon.rpc_access_info(client = self.get_signature())
cookie = res.cookie
loop_time = time.time()
res = daemon.rpc_access_data()
assert len(res.entries) > 0
e = [x for x in res.entries if x['client'] == self.public_key]
assert len(e) == 1
e = e[0]
assert e.nonces_stale == 0
assert e.nonces_bad == found_invalid
assert e.nonces_good == found_valid
assert e.nonces_dupe == 0
ok = False
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature())
except:
ok = True
assert ok
res = daemon.rpc_access_data()
assert len(res.entries) > 0
e = [x for x in res.entries if x['client'] == self.public_key]
assert len(e) == 1
e = e[0]
assert e.nonces_stale == 0
assert e.nonces_bad == found_invalid
assert e.nonces_good == found_valid
assert e.nonces_dupe == 1
# find stales without updating cookie, one within 5 seconds (accepted), one later (rejected)
res = daemon.rpc_access_info(client = self.get_signature())
cookie = res.cookie # let the daemon update its timestamp, but use old cookie
found_close_stale = 0
found_late_stale = 0
loop_time = time.time()
while found_close_stale == 0 or found_late_stale == 0:
nonce += 1
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature())
found_close_stale += 1
found_valid += 1
time.sleep(15) # now we've got an early stale, wait till they become late stales
except Exception as e:
#if e[0]['error']['code'] == -18: # stale
if "'code': -18" in str(e): # stale (ugly version, but also works with python 3)
found_late_stale += 1
else:
found_invalid += 1
assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails
if time.time() >= loop_time + 10:
res = daemon.rpc_access_info(client = self.get_signature())
# cookie = res.cookie # let the daemon update its timestamp, but use old cookie
loop_time = time.time()
res = daemon.rpc_access_data()
assert len(res.entries) > 0
e = [x for x in res.entries if x['client'] == self.public_key]
assert len(e) == 1
e = e[0]
assert e.nonces_stale == found_late_stale # close stales are accepted, don't count here
assert e.nonces_bad == found_invalid
assert e.nonces_good == found_valid
assert e.nonces_dupe == 1
# find very stale with old cookie (rejected)
res = daemon.rpc_access_info(client = self.get_signature())
nonce += 1
ok = False
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = old_cookie, client = self.get_signature())
except:
found_late_stale += 1
ok = True
assert ok
res = daemon.rpc_access_data()
assert len(res.entries) > 0
e = [x for x in res.entries if x['client'] == self.public_key]
assert len(e) == 1
e = e[0]
assert e.nonces_stale == found_late_stale
assert e.nonces_bad == found_invalid
assert e.nonces_good == found_valid
assert e.nonces_dupe == 1
def test_access_payment(self):
print('Testing access payment')
daemon = Daemon(idx=1)
wallet = Wallet(idx=3)
# Try random nonces till we find one that's valid so we get a load of credits
res = daemon.rpc_access_info(client = self.get_signature())
credits = res.credits
cookie = res.cookie
nonce = 0
while credits <= 100:
nonce += 1
try:
res = daemon.rpc_access_submit_nonce(nonce = nonce, cookie = cookie, client = self.get_signature())
break
except:
pass
assert nonce < 1000 # can't find both valid and invalid -> the RPC probably fails
res = daemon.rpc_access_info(client = self.get_signature())
credits = res.credits
assert credits > 0
res = daemon.get_info(client = self.get_signature())
assert res.credits == credits - 1
credits = res.credits
res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 100)
block_hashes = res.blocks
# ask for 1 block -> 1 credit
res = daemon.getblockheadersrange(0, 0, client = self.get_signature())
assert res.credits == credits - 1
credits = res.credits
# ask for 100 blocks -> >1 credit
res = daemon.getblockheadersrange(1, 100, client = self.get_signature())
assert res.credits < credits - 1
credits = res.credits
# external users
res = daemon.rpc_access_pay(payment = 1, paying_for = 'foo', client = self.get_signature())
assert res.credits == credits - 1
res = daemon.rpc_access_pay(payment = 4, paying_for = 'bar', client = self.get_signature())
assert res.credits == credits - 5
res = daemon.rpc_access_pay(payment = credits, paying_for = 'baz', client = self.get_signature())
assert "PAYMENT REQUIRED" in res.status
res = daemon.rpc_access_pay(payment = 2, paying_for = 'quux', client = self.get_signature())
assert res.credits == credits - 7
res = daemon.rpc_access_pay(payment = 3, paying_for = 'bar', client = self.get_signature())
assert res.credits == credits - 10
# that should be rejected because its cost is massive
ok = False
try: res = daemon.get_output_histogram(amounts = [], client = self.get_signature())
except Exception as e: print('e: ' + str(e)); ok = "PAYMENT REQUIRED" in e.status
assert ok or "PAYMENT REQUIRED" in res.status
def test_access_account(self):
print('Testing access account')
daemon = Daemon(idx=1)
wallet = Wallet(idx=3)
res = daemon.rpc_access_info(client = self.get_signature())
credits = res.credits
res = daemon.rpc_access_account(self.get_signature(), 0)
assert res.credits == credits
res = daemon.rpc_access_account(self.get_signature(), 50)
assert res.credits == credits + 50
res = daemon.rpc_access_account(self.get_signature(), -10)
assert res.credits == credits + 40
res = daemon.rpc_access_account(self.get_signature(), -(credits + 50))
assert res.credits == 0
res = daemon.rpc_access_account(self.get_signature(), 2**63 - 5)
assert res.credits == 2**63 - 5
res = daemon.rpc_access_account(self.get_signature(), 2**63 - 1)
assert res.credits == 2**64 - 6
res = daemon.rpc_access_account(self.get_signature(), 2)
assert res.credits == 2**64 - 4
res = daemon.rpc_access_account(self.get_signature(), 8)
assert res.credits == 2**64 - 1
res = daemon.rpc_access_account(self.get_signature(), -1)
assert res.credits == 2**64 - 2
res = daemon.rpc_access_account(self.get_signature(), -(2**63 - 1))
assert res.credits == 2**64 - 2 -(2**63 - 1)
res = daemon.rpc_access_account(self.get_signature(), -(2**63 - 1))
assert res.credits == 0
def test_free_access(self):
print('Testing free access')
daemon = Daemon(idx=0)
wallet = Wallet(idx=0)
res = daemon.rpc_access_info(client = self.get_signature())
assert res.credits_per_hash_found == 0
assert res.diff == 0
assert res.credits == 0
res = daemon.get_info(client = self.get_signature())
assert res.credits == 0
# any nonce will do here
res = daemon.rpc_access_submit_nonce(nonce = 0, cookie = 0, client = self.get_signature())
assert res.credits == 0
class Guard:
def __enter__(self):
for i in range(4):
Wallet(idx = i).auto_refresh(False)
def __exit__(self, exc_type, exc_value, traceback):
for i in range(4):
Wallet(idx = i).auto_refresh(True)
if __name__ == '__main__':
with Guard() as guard:
RPCPaymentTest().run_test()
-39
View File
@@ -75,45 +75,6 @@ std::unique_ptr<RpcServerBundle> initialise_rpc_server(cryptonote::core& dummy_c
bundle->dummy_p2p = std::make_unique<nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>>(*bundle->proto_handler);
bundle->rpc = std::make_unique<cryptonote::core_rpc_server>(dummy_core, *bundle->dummy_p2p);
// Set up dummy variable map for rpc initialisation with payment
if (need_payment) {
boost::program_options::variables_map vm;
boost::program_options::options_description desc{"fuzz"};
command_line::add_arg(desc, cryptonote::arg_data_dir);
command_line::add_arg(desc, cryptonote::arg_testnet_on);
command_line::add_arg(desc, cryptonote::arg_stagenet_on);
cryptonote::core_rpc_server::init_options(desc);
// Generate random address and use it if it is a valid address with valid format
std::string address_arg;
if (provider.remaining_bytes() > 95) {
std::string random_str = provider.ConsumeBytesAsString(95);
if (!random_str.empty() && std::all_of(random_str.begin(), random_str.end(), [](char c) {
return isalnum(c);
})) {
address_arg = "--rpc-payment-address=" + random_str;
}
}
// Fall back to default hardcoded address if generated address is invalid
if (address_arg.empty()) {
address_arg = "--rpc-payment-address=44AFFq5kSiGBoZKfRLKFY7bUuS5JxqLkZ3Zf1diYv5ZdfTP7hS5gZtSGdgjNXmYGFzRiV3yTgF8Yf4zrhGcq14D3z8PUnHT";
}
// Provide needed payment related configuration for init of core_rpc_server
std::vector<const char*> argv = {
"fuzz",
address_arg.c_str()
};
boost::program_options::store(boost::program_options::parse_command_line(argv.size(), argv.data(), desc), vm);
boost::program_options::notify(vm);
bool success = bundle->rpc->init(vm, true, "18089", true, "");
if (!success) {
// Revert back to a fresh core_rpc_server if payment module init is failed
bundle->rpc = std::make_unique<cryptonote::core_rpc_server>(dummy_core, *bundle->dummy_p2p);
}
}
return bundle;
}
-63
View File
@@ -1,6 +1,5 @@
#include "rpc_endpoints.h"
#include "initialisation.h"
#include "rpc/rpc_payment_signature.h"
#include <cstring>
#include <map>
@@ -799,62 +798,6 @@ void fuzz_get_txids_loose(cryptonote::core_rpc_server& rpc, FuzzedDataProvider&
rpc.on_get_txids_loose(req, res, error_resp, &ctx);
}
void fuzz_rpc_access_info(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
cryptonote::COMMAND_RPC_ACCESS_INFO::request req;
cryptonote::COMMAND_RPC_ACCESS_INFO::response res;
req.client = "fuzz";
rpc.on_rpc_access_info(req, res, error_resp, &ctx);
}
void fuzz_rpc_access_submit_nonce(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::request req;
cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::response res;
req.client = "fuzz";
req.nonce = provider.ConsumeIntegral<uint32_t>();
req.cookie = provider.ConsumeIntegral<uint32_t>();
rpc.on_rpc_access_submit_nonce(req, res, error_resp, &ctx);
}
void fuzz_rpc_access_pay(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
cryptonote::COMMAND_RPC_ACCESS_PAY::request req;
cryptonote::COMMAND_RPC_ACCESS_PAY::response res;
req.client = "fuzz";
req.paying_for = provider.ConsumeRandomLengthString(32);
req.payment = provider.ConsumeIntegral<uint64_t>();
rpc.on_rpc_access_pay(req, res, error_resp, &ctx);
}
void fuzz_rpc_access_tracking(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
cryptonote::COMMAND_RPC_ACCESS_TRACKING::request req;
cryptonote::COMMAND_RPC_ACCESS_TRACKING::response res;
req.clear = provider.ConsumeBool();
rpc.on_rpc_access_tracking(req, res, error_resp, &ctx);
}
void fuzz_rpc_access_data(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
cryptonote::COMMAND_RPC_ACCESS_DATA::request req;
cryptonote::COMMAND_RPC_ACCESS_DATA::response res;
rpc.on_rpc_access_data(req, res, error_resp, &ctx);
}
void fuzz_rpc_access_account(cryptonote::core_rpc_server& rpc, FuzzedDataProvider& provider) {
cryptonote::COMMAND_RPC_ACCESS_ACCOUNT::request req;
cryptonote::COMMAND_RPC_ACCESS_ACCOUNT::response res;
req.client = "fuzz";
req.delta_balance = provider.ConsumeIntegral<int64_t>();
rpc.on_rpc_access_account(req, res, error_resp, &ctx);
}
// Maps storing all fuzzing functions
std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> priority_fuzz_targets = {
{0, fuzz_get_blocks},
@@ -929,11 +872,5 @@ std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvide
{63, fuzz_flush_txpool},
{64, fuzz_flush_cache},
{65, fuzz_get_txids_loose},
{66, fuzz_rpc_access_info},
{67, fuzz_rpc_access_submit_nonce},
{68, fuzz_rpc_access_pay},
{69, fuzz_rpc_access_tracking},
{70, fuzz_rpc_access_data},
{71, fuzz_rpc_access_account},
// {72, fuzz_prune_blockchain},
};
-6
View File
@@ -79,12 +79,6 @@ void fuzz_get_output_distribution(cryptonote::core_rpc_server&, FuzzedDataProvid
void fuzz_prune_blockchain(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_flush_cache(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_get_txids_loose(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_rpc_access_info(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_rpc_access_submit_nonce(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_rpc_access_pay(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_rpc_access_tracking(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_rpc_access_data(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_rpc_access_account(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
void fuzz_get_height(cryptonote::core_rpc_server&, FuzzedDataProvider& provider);
extern std::map<int, std::function<void(cryptonote::core_rpc_server&, FuzzedDataProvider&)>> priority_fuzz_targets;
-1
View File
@@ -52,7 +52,6 @@ complete -c monero-wallet-cli -l do-not-relay -d "The newly created transaction
complete -c monero-wallet-cli -l create-address-file -d "Create an address file for new wallets"
complete -c monero-wallet-cli -l subaddress-lookahead -r -d "Set subaddress lookahead sizes to <major>:<minor>"
complete -c monero-wallet-cli -l use-english-language-names -d "Display English language names"
complete -c monero-wallet-cli -l rpc-client-secret-key -r -d "Set RPC client secret key for RPC payments"
complete -c monero-wallet-cli -l log-file -r -F -d "Specify log file"
complete -c monero-wallet-cli -l log-level -r -a "0 1 2 3 4" -d "0-4 or categories"
complete -c monero-wallet-cli -l max-log-file-size -r -d "Specify maximum log file size [B]. Default: 104850000"
-1
View File
@@ -54,7 +54,6 @@ complete -c monero-wallet-rpc -l wallet-file -r -F -d "Use wallet <arg>"
complete -c monero-wallet-rpc -l generate-from-json -r -k -a "(__fish_complete_suffix .json)" -d "Generate wallet from JSON format file"
complete -c monero-wallet-rpc -l wallet-dir -r -F -d "Directory for newly created wallets"
complete -c monero-wallet-rpc -l prompt-for-password -d "Prompts for password when not provided"
complete -c monero-wallet-rpc -l rpc-client-secret-key -r -d "Set RPC client secret key for RPC payments"
complete -c monero-wallet-rpc -l detach -d "Run as daemon"
complete -c monero-wallet-rpc -l pidfile -r -F -d "File path to write the daemon's PID to (optional, requires --detach)"
complete -c monero-wallet-rpc -l non-interactive -d "Run non-interactive"
-4
View File
@@ -103,10 +103,6 @@ complete -c monerod -l rpc-ssl-allowed-fingerprints -r -d "List of certificate f
complete -c monerod -l rpc-ssl-allow-chained -d "Allow user (via --rpc-ssl-certificates) chain certificates"
complete -c monerod -l disable-rpc-ban -d "Do not ban hosts on RPC errors"
complete -c monerod -l rpc-ssl-allow-any-cert -d "Allow any peer certificate"
complete -c monerod -l rpc-payment-address -r -d "Restrict RPC to clients sending micropayment to this address"
complete -c monerod -l rpc-payment-difficulty -r -d "Restrict RPC to clients sending micropayment at this difficulty. Default: 1000"
complete -c monerod -l rpc-payment-credits -r -d "Restrict RPC to clients sending micropayment, yields that many credits per payment. Default: 100"
complete -c monerod -l rpc-payment-allow-free-loopback -d "Allow free access from the loopback address (ie, the local host)"
complete -c monerod -l rpc-max-connections-per-public-ip -d "Max RPC connections per public IP permitted. Default: 3"
complete -c monerod -l rpc-max-connections-per-private-ip -d "Max RPC connections per private and localhost IP permitted. Default: 25"
complete -c monerod -l rpc-max-connections -d "Max RPC connections permitted. Default: 100"
-70
View File
@@ -612,73 +612,3 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(sync_txpool)
def rpc_access_info(self, client):
rpc_access_info = {
'method': 'rpc_access_info',
'params': {
'client': client,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(rpc_access_info)
def rpc_access_submit_nonce(self, client, nonce, cookie):
rpc_access_submit_nonce = {
'method': 'rpc_access_submit_nonce',
'params': {
'client': client,
'nonce': nonce,
'cookie': cookie,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(rpc_access_submit_nonce)
def rpc_access_pay(self, client, paying_for, payment):
rpc_access_pay = {
'method': 'rpc_access_pay',
'params': {
'client': client,
'paying_for': paying_for,
'payment': payment,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(rpc_access_pay)
def rpc_access_tracking(self, clear = False):
rpc_access_tracking = {
'method': 'rpc_access_tracking',
'params': {
'clear': clear,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(rpc_access_tracking)
def rpc_access_data(self):
rpc_access_data = {
'method': 'rpc_access_data',
'params': {
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(rpc_access_data)
def rpc_access_account(self, client, delta_balance = 0):
rpc_access_account = {
'method': 'rpc_access_account',
'params': {
'client': client,
'delta_balance': delta_balance,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(rpc_access_account)