mirror of
https://github.com/monero-project/monero.git
synced 2026-06-12 19:11:36 -07:00
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:
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -170,8 +170,6 @@ public:
|
||||
const std::string &password,
|
||||
const std::string &proxy);
|
||||
|
||||
bool rpc_payments();
|
||||
|
||||
bool flush_cache(bool invalid_blocks);
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user