diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/rpc/bootstrap_daemon.cpp | 40 | ||||
-rw-r--r-- | src/rpc/bootstrap_daemon.h | 34 | ||||
-rw-r--r-- | src/rpc/bootstrap_node_selector.cpp | 117 | ||||
-rw-r--r-- | src/rpc/bootstrap_node_selector.h | 103 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 81 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.h | 2 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 2 | ||||
-rw-r--r-- | src/rpc/daemon_handler.cpp | 29 | ||||
-rw-r--r-- | src/rpc/daemon_handler.h | 5 | ||||
-rw-r--r-- | src/rpc/message.cpp | 29 | ||||
-rw-r--r-- | src/rpc/message.h | 11 | ||||
-rw-r--r-- | src/rpc/rpc_args.cpp | 4 | ||||
-rw-r--r-- | src/rpc/rpc_handler.h | 3 | ||||
-rw-r--r-- | src/rpc/rpc_payment.cpp | 4 | ||||
-rw-r--r-- | src/rpc/rpc_payment_signature.cpp | 5 | ||||
-rw-r--r-- | src/rpc/zmq_server.cpp | 10 |
17 files changed, 380 insertions, 100 deletions
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 65d88b57e..fe5e5a85b 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -35,6 +35,7 @@ set(rpc_base_sources set(rpc_sources bootstrap_daemon.cpp + bootstrap_node_selector.cpp core_rpc_server.cpp rpc_payment.cpp rpc_version_str.cpp diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp index c97b2c95a..6a0833f19 100644 --- a/src/rpc/bootstrap_daemon.cpp +++ b/src/rpc/bootstrap_daemon.cpp @@ -2,6 +2,8 @@ #include <stdexcept> +#include <boost/thread/locks.hpp> + #include "crypto/crypto.h" #include "cryptonote_core/cryptonote_core.h" #include "misc_log_ex.h" @@ -12,15 +14,22 @@ namespace cryptonote { - bootstrap_daemon::bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node) - : m_get_next_public_node(get_next_public_node) + bootstrap_daemon::bootstrap_daemon( + std::function<std::map<std::string, bool>()> get_public_nodes, + bool rpc_payment_enabled) + : m_selector(new bootstrap_node::selector_auto(std::move(get_public_nodes))) + , m_rpc_payment_enabled(rpc_payment_enabled) { } - bootstrap_daemon::bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials) - : bootstrap_daemon(nullptr) + bootstrap_daemon::bootstrap_daemon( + const std::string &address, + boost::optional<epee::net_utils::http::login> credentials, + bool rpc_payment_enabled) + : m_selector(nullptr) + , m_rpc_payment_enabled(rpc_payment_enabled) { - if (!set_server(address, credentials)) + if (!set_server(address, std::move(credentials))) { throw std::runtime_error("invalid bootstrap daemon address or credentials"); } @@ -54,11 +63,16 @@ namespace cryptonote return res.height; } - bool bootstrap_daemon::handle_result(bool success) + bool bootstrap_daemon::handle_result(bool success, const std::string &status) { - if (!success && m_get_next_public_node) + const bool failed = !success || (!m_rpc_payment_enabled && status == CORE_RPC_STATUS_PAYMENT_REQUIRED); + if (failed && m_selector) { + const std::string current_address = address(); m_http_client.disconnect(); + + const boost::unique_lock<boost::mutex> lock(m_selector_mutex); + m_selector->handle_result(current_address, !failed); } return success; @@ -79,14 +93,18 @@ namespace cryptonote bool bootstrap_daemon::switch_server_if_needed() { - if (!m_get_next_public_node || m_http_client.is_connected()) + if (m_http_client.is_connected() || !m_selector) { return true; } - const boost::optional<std::string> address = m_get_next_public_node(); - if (address) { - return set_server(*address); + boost::optional<bootstrap_node::node_info> node; + { + const boost::unique_lock<boost::mutex> lock(m_selector_mutex); + node = m_selector->next_node(); + } + if (node) { + return set_server(node->address, node->credentials); } return false; diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h index 6276b1b21..bedc255b5 100644 --- a/src/rpc/bootstrap_daemon.h +++ b/src/rpc/bootstrap_daemon.h @@ -1,26 +1,34 @@ #pragma once #include <functional> -#include <vector> +#include <map> #include <boost/optional/optional.hpp> +#include <boost/thread/mutex.hpp> #include <boost/utility/string_ref.hpp> #include "net/http_client.h" #include "storages/http_abstract_invoke.h" +#include "bootstrap_node_selector.h" + namespace cryptonote { class bootstrap_daemon { public: - bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node); - bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials); + bootstrap_daemon( + std::function<std::map<std::string, bool>()> get_public_nodes, + bool rpc_payment_enabled); + bootstrap_daemon( + const std::string &address, + boost::optional<epee::net_utils::http::login> credentials, + bool rpc_payment_enabled); std::string address() const noexcept; boost::optional<uint64_t> get_height(); - bool handle_result(bool success); + bool handle_result(bool success, const std::string &status); template <class t_request, class t_response> bool invoke_http_json(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct) @@ -30,7 +38,8 @@ namespace cryptonote return false; } - return handle_result(epee::net_utils::invoke_http_json(uri, out_struct, result_struct, m_http_client)); + const bool result = epee::net_utils::invoke_http_json(uri, out_struct, result_struct, m_http_client); + return handle_result(result, result_struct.status); } template <class t_request, class t_response> @@ -41,7 +50,8 @@ namespace cryptonote return false; } - return handle_result(epee::net_utils::invoke_http_bin(uri, out_struct, result_struct, m_http_client)); + const bool result = epee::net_utils::invoke_http_bin(uri, out_struct, result_struct, m_http_client); + return handle_result(result, result_struct.status); } template <class t_request, class t_response> @@ -52,7 +62,13 @@ namespace cryptonote return false; } - return handle_result(epee::net_utils::invoke_http_json_rpc("/json_rpc", std::string(command_name.begin(), command_name.end()), out_struct, result_struct, m_http_client)); + const bool result = epee::net_utils::invoke_http_json_rpc( + "/json_rpc", + std::string(command_name.begin(), command_name.end()), + out_struct, + result_struct, + m_http_client); + return handle_result(result, result_struct.status); } private: @@ -61,7 +77,9 @@ namespace cryptonote private: epee::net_utils::http::http_simple_client m_http_client; - std::function<boost::optional<std::string>()> m_get_next_public_node; + const bool m_rpc_payment_enabled; + const std::unique_ptr<bootstrap_node::selector> m_selector; + boost::mutex m_selector_mutex; }; } diff --git a/src/rpc/bootstrap_node_selector.cpp b/src/rpc/bootstrap_node_selector.cpp new file mode 100644 index 000000000..34845060e --- /dev/null +++ b/src/rpc/bootstrap_node_selector.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2020, 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 "bootstrap_node_selector.h" + +#include "crypto/crypto.h" + +namespace cryptonote +{ +namespace bootstrap_node +{ + + void selector_auto::node::handle_result(bool success) + { + if (!success) + { + fails = std::min(std::numeric_limits<size_t>::max() - 2, fails) + 2; + } + else + { + fails = std::max(std::numeric_limits<size_t>::min() + 2, fails) - 2; + } + } + + void selector_auto::handle_result(const std::string &address, bool success) + { + auto &nodes_by_address = m_nodes.get<by_address>(); + const auto it = nodes_by_address.find(address); + if (it != nodes_by_address.end()) + { + nodes_by_address.modify(it, [success](node &entry) { + entry.handle_result(success); + }); + } + } + + boost::optional<node_info> selector_auto::next_node() + { + if (!has_at_least_one_good_node()) + { + append_new_nodes(); + } + + if (m_nodes.empty()) + { + return {}; + } + + auto node = m_nodes.get<by_fails>().begin(); + const size_t count = std::distance(node, m_nodes.get<by_fails>().upper_bound(node->fails)); + std::advance(node, crypto::rand_idx(count)); + + return {{node->address, {}}}; + } + + bool selector_auto::has_at_least_one_good_node() const + { + return !m_nodes.empty() && m_nodes.get<by_fails>().begin()->fails == 0; + } + + void selector_auto::append_new_nodes() + { + bool updated = false; + + for (const auto &node : m_get_nodes()) + { + const auto &address = node.first; + const auto &white = node.second; + const size_t initial_score = white ? 0 : 1; + updated |= m_nodes.get<by_address>().insert({address, initial_score}).second; + } + + if (updated) + { + truncate(); + } + } + + void selector_auto::truncate() + { + const size_t total = m_nodes.size(); + if (total > m_max_nodes) + { + auto &nodes_by_fails = m_nodes.get<by_fails>(); + auto from = nodes_by_fails.rbegin(); + std::advance(from, total - m_max_nodes); + nodes_by_fails.erase(from.base(), nodes_by_fails.end()); + } + } + +} +} diff --git a/src/rpc/bootstrap_node_selector.h b/src/rpc/bootstrap_node_selector.h new file mode 100644 index 000000000..fc993719b --- /dev/null +++ b/src/rpc/bootstrap_node_selector.h @@ -0,0 +1,103 @@ +// Copyright (c) 2020, 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 <functional> +#include <limits> +#include <map> +#include <string> +#include <utility> + +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/member.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/optional/optional.hpp> + +#include "net/http_client.h" + +namespace cryptonote +{ +namespace bootstrap_node +{ + + struct node_info + { + std::string address; + boost::optional<epee::net_utils::http::login> credentials; + }; + + struct selector + { + virtual void handle_result(const std::string &address, bool success) = 0; + virtual boost::optional<node_info> next_node() = 0; + }; + + class selector_auto : public selector + { + public: + selector_auto(std::function<std::map<std::string, bool>()> get_nodes, size_t max_nodes = 1000) + : m_get_nodes(std::move(get_nodes)) + , m_max_nodes(max_nodes) + {} + + void handle_result(const std::string &address, bool success) final; + boost::optional<node_info> next_node() final; + + private: + bool has_at_least_one_good_node() const; + void append_new_nodes(); + void truncate(); + + private: + struct node + { + std::string address; + size_t fails; + + void handle_result(bool success); + }; + + struct by_address {}; + struct by_fails {}; + + typedef boost::multi_index_container< + node, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique<boost::multi_index::tag<by_address>, boost::multi_index::member<node, std::string, &node::address>>, + boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_fails>, boost::multi_index::member<node, size_t, &node::fails>> + > + > nodes_list; + + const std::function<std::map<std::string, bool>()> m_get_nodes; + const size_t m_max_nodes; + nodes_list m_nodes; + }; + +} +} diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 8e5207e82..9b0eeb1f1 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -178,7 +178,7 @@ namespace cryptonote return set_bootstrap_daemon(address, credentials); } //------------------------------------------------------------------------------------------------------------------------------ - boost::optional<std::string> core_rpc_server::get_random_public_node() + std::map<std::string, bool> core_rpc_server::get_public_nodes(uint32_t credits_per_hash_threshold/* = 0*/) { COMMAND_RPC_GET_PUBLIC_NODES::request request; COMMAND_RPC_GET_PUBLIC_NODES::response response; @@ -187,47 +187,51 @@ namespace cryptonote request.white = true; if (!on_get_public_nodes(request, response) || response.status != CORE_RPC_STATUS_OK) { - return boost::none; + return {}; } - const auto get_random_node_address = [](const std::vector<public_node>& public_nodes) -> std::string { - const auto& random_node = public_nodes[crypto::rand_idx(public_nodes.size())]; - const auto address = random_node.host + ":" + std::to_string(random_node.rpc_port); - return address; - }; - - if (!response.white.empty()) - { - return get_random_node_address(response.white); - } - - MDEBUG("No white public node found, checking gray peers"); + std::map<std::string, bool> result; - if (!response.gray.empty()) - { - return get_random_node_address(response.gray); - } + const auto append = [&result, &credits_per_hash_threshold](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)); + } + } + }; - MERROR("Failed to find any suitable public node"); + append(response.white, true); + append(response.gray, false); - return boost::none; + return result; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials) { 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); } else if (address == "auto") { - m_bootstrap_daemon.reset(new bootstrap_daemon([this]{ return get_random_public_node(); })); + auto get_nodes = [this, credits_per_hash_threshold]() { + return get_public_nodes(credits_per_hash_threshold); + }; + m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled)); } else { - m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials)); + m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled)); } m_should_use_bootstrap_daemon = m_bootstrap_daemon.get() != nullptr; @@ -434,7 +438,7 @@ namespace cryptonote store_difficulty(m_core.get_blockchain_storage().get_difficulty_for_next_block(), res.difficulty, res.wide_difficulty, res.difficulty_top64); res.target = m_core.get_blockchain_storage().get_difficulty_target(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase - res.tx_pool_size = m_core.get_pool_transactions_count(); + res.tx_pool_size = m_core.get_pool_transactions_count(!restricted); res.alt_blocks_count = restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count(); uint64_t total_conn = restricted ? 0 : m_p2p.get_public_connections_count(); res.outgoing_connections_count = restricted ? 0 : m_p2p.get_public_outgoing_connections_count(); @@ -1115,6 +1119,8 @@ namespace cryptonote } res.sanity_check_failed = false; + const bool restricted = m_restricted && ctx; + tx_verification_context tvc{}; if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (req.do_not_relay ? relay_method::none : relay_method::local), false) || tvc.m_verifivation_failed) { @@ -1134,8 +1140,6 @@ namespace cryptonote add_reason(reason, "overspend"); if ((res.fee_too_low = tvc.m_fee_too_low)) add_reason(reason, "fee too low"); - if ((res.not_rct = tvc.m_not_rct)) - add_reason(reason, "tx is not ringct"); if ((res.too_few_outputs = tvc.m_too_few_outputs)) add_reason(reason, "too few outputs"); const std::string punctuation = reason.empty() ? "" : ": "; @@ -1150,7 +1154,7 @@ namespace cryptonote return true; } - if(!tvc.m_should_be_relayed) + if(tvc.m_relay == relay_method::none) { LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); res.reason = "Not relayed"; @@ -1160,8 +1164,8 @@ namespace cryptonote } NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); + r.txs.push_back(std::move(tx_blob)); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid, relay_method::local); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = CORE_RPC_STATUS_OK; return true; @@ -1421,12 +1425,13 @@ namespace cryptonote const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + const bool allow_sensitive = !request_has_rpc_origin || !restricted; - size_t n_txes = m_core.get_pool_transactions_count(); + 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, !request_has_rpc_origin || !restricted); + 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); } @@ -1446,12 +1451,13 @@ namespace cryptonote const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + const bool allow_sensitive = !request_has_rpc_origin || !restricted; - size_t n_txes = m_core.get_pool_transactions_count(); + 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, !request_has_rpc_origin || !restricted); + m_core.get_pool_transaction_hashes(res.tx_hashes, allow_sensitive); } res.status = CORE_RPC_STATUS_OK; @@ -1469,13 +1475,14 @@ namespace cryptonote const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + const bool allow_sensitive = !request_has_rpc_origin || !restricted; - size_t n_txes = m_core.get_pool_transactions_count(); + 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, !request_has_rpc_origin || !restricted); + m_core.get_pool_transaction_hashes(tx_hashes, allow_sensitive); res.tx_hashes.reserve(tx_hashes.size()); for (const crypto::hash &tx_hash: tx_hashes) res.tx_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash)); @@ -1943,7 +1950,7 @@ namespace cryptonote if (*bootstrap_daemon_height < target_height) { MINFO("Bootstrap daemon is out of sync"); - return m_bootstrap_daemon->handle_result(false); + return m_bootstrap_daemon->handle_result(false, {}); } uint64_t top_height = m_core.get_current_blockchain_height(); @@ -2774,8 +2781,8 @@ namespace cryptonote if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) { NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(txblob); - m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); + r.txs.push_back(std::move(txblob)); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid, relay_method::local); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes } else diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index d82ab6af4..3c404bbd8 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -267,7 +267,7 @@ private: //utils uint64_t get_block_reward(const block& blk); bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash); - boost::optional<std::string> get_random_public_node(); + std::map<std::string, bool> get_public_nodes(uint32_t credits_per_hash_threshold = 0); bool set_bootstrap_daemon(const std::string &address, const std::string &username_password); bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials); enum invoke_http_mode { JON, BIN, JON_RPC }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index dbb1d4472..a3c187c24 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -588,7 +588,6 @@ namespace cryptonote bool too_big; bool overspend; bool fee_too_low; - bool not_rct; bool too_few_outputs; bool sanity_check_failed; @@ -603,7 +602,6 @@ namespace cryptonote KV_SERIALIZE(too_big) KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) - KV_SERIALIZE(not_rct) KV_SERIALIZE(too_few_outputs) KV_SERIALIZE(sanity_check_failed) END_KV_SERIALIZE_MAP() diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 125688ba5..de0510fec 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -33,6 +33,7 @@ #include <stdexcept> #include <boost/uuid/nil_generator.hpp> +#include <boost/utility/string_ref.hpp> // likely included by daemon_handler.h's includes, // but including here for clarity #include "cryptonote_core/cryptonote_core.h" @@ -48,7 +49,7 @@ namespace rpc { namespace { - using handler_function = std::string(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& msg); + using handler_function = epee::byte_slice(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& msg); struct handler_map { const char* method_name; @@ -66,7 +67,7 @@ namespace rpc } template<typename Message> - std::string handle_message(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& parameters) + epee::byte_slice handle_message(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& parameters) { typename Message::Request request{}; request.fromJson(parameters); @@ -349,10 +350,10 @@ namespace rpc res.error_details = "Invalid hex"; return; } - handleTxBlob(tx_blob, req.relay, res); + handleTxBlob(std::move(tx_blob), req.relay, res); } - void DaemonHandler::handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res) + void DaemonHandler::handleTxBlob(std::string&& tx_blob, bool relay, SendRawTx::Response& res) { if (!m_p2p.get_payload_object().is_synchronized()) { @@ -410,11 +411,6 @@ namespace rpc if (!res.error_details.empty()) res.error_details += " and "; res.error_details += "fee too low"; } - if (tvc.m_not_rct) - { - if (!res.error_details.empty()) res.error_details += " and "; - res.error_details += "tx is not ringct"; - } if (tvc.m_too_few_outputs) { if (!res.error_details.empty()) res.error_details += " and "; @@ -428,7 +424,7 @@ namespace rpc return; } - if(!tvc.m_should_be_relayed || !relay) + if(tvc.m_relay == relay_method::none || !relay) { MERROR("[SendRawTx]: tx accepted, but not relayed"); res.error_details = "Not relayed"; @@ -439,8 +435,8 @@ namespace rpc } NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); + r.txs.push_back(std::move(tx_blob)); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid, relay_method::local); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = Message::STATUS_OK; @@ -908,7 +904,7 @@ namespace rpc return true; } - std::string DaemonHandler::handle(const std::string& request) + epee::byte_slice DaemonHandler::handle(const std::string& request) { MDEBUG("Handling RPC request: " << request); @@ -921,8 +917,11 @@ namespace rpc if (matched_handler == std::end(handlers) || matched_handler->method_name != request_type) return BAD_REQUEST(request_type, req_full.getID()); - std::string response = matched_handler->call(*this, req_full.getID(), req_full.getMessage()); - MDEBUG("Returning RPC response: " << response); + epee::byte_slice response = matched_handler->call(*this, req_full.getID(), req_full.getMessage()); + + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Returning RPC response: " << response_view); + return response; } catch (const std::exception& e) diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index c33f608ab..b797b1155 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -28,6 +28,7 @@ #pragma once +#include "byte_slice.h" #include "daemon_messages.h" #include "daemon_rpc_version.h" #include "rpc_handler.h" @@ -132,13 +133,13 @@ class DaemonHandler : public RpcHandler void handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res); - std::string handle(const std::string& request); + epee::byte_slice handle(const std::string& request) override final; private: bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response); - void handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res); + void handleTxBlob(std::string&& tx_blob, bool relay, SendRawTx::Response& res); cryptonote::core& m_core; t_p2p& m_p2p; diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index a3df7fb56..0d8983cb1 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -50,6 +50,16 @@ constexpr const char id_field[] = "id"; constexpr const char method_field[] = "method"; constexpr const char params_field[] = "params"; constexpr const char result_field[] = "result"; + +const rapidjson::Value& get_method_field(const rapidjson::Value& src) +{ + const auto member = src.FindMember(method_field); + if (member == src.MemberEnd()) + throw cryptonote::json::MISSING_KEY{method_field}; + if (!member->value.IsString()) + throw cryptonote::json::WRONG_TYPE{"Expected string"}; + return member->value; +} } void Message::toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const @@ -81,7 +91,7 @@ FullMessage::FullMessage(const std::string& json_string, bool request) if (request) { - OBJECT_HAS_MEMBER_OR_THROW(doc, method_field) + get_method_field(doc); // throws on errors OBJECT_HAS_MEMBER_OR_THROW(doc, params_field) } else @@ -95,8 +105,7 @@ FullMessage::FullMessage(const std::string& json_string, bool request) std::string FullMessage::getRequestType() const { - OBJECT_HAS_MEMBER_OR_THROW(doc, method_field) - return doc[method_field].GetString(); + return get_method_field(doc).GetString(); } const rapidjson::Value& FullMessage::getMessage() const @@ -140,7 +149,7 @@ cryptonote::rpc::error FullMessage::getError() return err; } -std::string FullMessage::getRequest(const std::string& request, const Message& message, const unsigned id) +epee::byte_slice FullMessage::getRequest(const std::string& request, const Message& message, const unsigned id) { rapidjson::StringBuffer buffer; { @@ -163,11 +172,11 @@ std::string FullMessage::getRequest(const std::string& request, const Message& m if (!dest.IsComplete()) throw std::logic_error{"Invalid JSON tree generated"}; } - return std::string{buffer.GetString(), buffer.GetSize()}; + return epee::byte_slice{{buffer.GetString(), buffer.GetSize()}}; } -std::string FullMessage::getResponse(const Message& message, const rapidjson::Value& id) +epee::byte_slice FullMessage::getResponse(const Message& message, const rapidjson::Value& id) { rapidjson::StringBuffer buffer; { @@ -198,17 +207,17 @@ std::string FullMessage::getResponse(const Message& message, const rapidjson::Va if (!dest.IsComplete()) throw std::logic_error{"Invalid JSON tree generated"}; } - return std::string{buffer.GetString(), buffer.GetSize()}; + return epee::byte_slice{{buffer.GetString(), buffer.GetSize()}}; } // convenience functions for bad input -std::string BAD_REQUEST(const std::string& request) +epee::byte_slice BAD_REQUEST(const std::string& request) { rapidjson::Value invalid; return BAD_REQUEST(request, invalid); } -std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id) +epee::byte_slice BAD_REQUEST(const std::string& request, const rapidjson::Value& id) { Message fail; fail.status = Message::STATUS_BAD_REQUEST; @@ -216,7 +225,7 @@ std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id) return FullMessage::getResponse(fail, id); } -std::string BAD_JSON(const std::string& error_details) +epee::byte_slice BAD_JSON(const std::string& error_details) { rapidjson::Value invalid; Message fail; diff --git a/src/rpc/message.h b/src/rpc/message.h index 4cbc84fe4..8156e232c 100644 --- a/src/rpc/message.h +++ b/src/rpc/message.h @@ -33,6 +33,7 @@ #include <rapidjson/writer.h> #include <string> +#include "byte_slice.h" #include "rpc/message_data_structs.h" namespace cryptonote @@ -85,8 +86,8 @@ namespace rpc cryptonote::rpc::error getError(); - static std::string getRequest(const std::string& request, const Message& message, unsigned id); - static std::string getResponse(const Message& message, const rapidjson::Value& id); + static epee::byte_slice getRequest(const std::string& request, const Message& message, unsigned id); + static epee::byte_slice getResponse(const Message& message, const rapidjson::Value& id); private: FullMessage() = default; @@ -99,10 +100,10 @@ namespace rpc // convenience functions for bad input - std::string BAD_REQUEST(const std::string& request); - std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id); + epee::byte_slice BAD_REQUEST(const std::string& request); + epee::byte_slice BAD_REQUEST(const std::string& request, const rapidjson::Value& id); - std::string BAD_JSON(const std::string& error_details); + epee::byte_slice BAD_JSON(const std::string& error_details); } // namespace rpc diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index dcb804d3e..9153e76ea 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -51,7 +51,7 @@ namespace cryptonote const std::vector<std::string> ssl_allowed_fingerprints = command_line::get_arg(vm, arg.rpc_ssl_allowed_fingerprints); std::vector<std::vector<uint8_t>> allowed_fingerprints{ ssl_allowed_fingerprints.size() }; - std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector); + std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex_locale::to_vector); for (const auto &fpr: allowed_fingerprints) { if (fpr.size() != SSL_FINGERPRINT_SIZE) diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index b81983d28..9a1c3fc12 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -32,6 +32,7 @@ #include <cstdint> #include <string> #include <vector> +#include "byte_slice.h" #include "crypto/hash.h" namespace cryptonote @@ -54,7 +55,7 @@ class RpcHandler RpcHandler() { } virtual ~RpcHandler() { } - virtual std::string handle(const std::string& request) = 0; + virtual epee::byte_slice handle(const std::string& request) = 0; static boost::optional<output_distribution_data> get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, const std::function<crypto::hash(uint64_t)> &get_hash, bool cumulative, uint64_t blockchain_height); diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index b363c27b2..2b9c19f57 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -54,8 +54,6 @@ #define DEFAULT_FLUSH_AGE (3600 * 24 * 180) // half a year #define DEFAULT_ZERO_FLUSH_AGE (60 * 2) // 2 minutes -#define RPC_PAYMENT_NONCE_TAIL 0x58 - namespace cryptonote { rpc_payment::client_info::client_info(): @@ -147,7 +145,7 @@ namespace cryptonote return false; char data[33]; memcpy(data, &client, 32); - data[32] = RPC_PAYMENT_NONCE_TAIL; + 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); diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp index 2e8b54b4f..559f3a1e9 100644 --- a/src/rpc/rpc_payment_signature.cpp +++ b/src/rpc/rpc_payment_signature.cpp @@ -102,6 +102,11 @@ namespace cryptonote MDEBUG("Timestamp is in the future"); return false; } + if (ts < now - TIMESTAMP_LEEWAY) + { + MDEBUG("Timestamp is too old"); + return false; + } return true; } } diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index 1ee55673e..91e4751d1 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -28,10 +28,13 @@ #include "zmq_server.h" +#include <boost/utility/string_ref.hpp> #include <chrono> #include <cstdint> #include <system_error> +#include "byte_slice.h" + namespace cryptonote { @@ -73,10 +76,11 @@ void ZmqServer::serve() { const std::string message = MONERO_UNWRAP(net::zmq::receive(socket.get())); MDEBUG("Received RPC request: \"" << message << "\""); - const std::string& response = handler.handle(message); + epee::byte_slice response = handler.handle(message); - MONERO_UNWRAP(net::zmq::send(epee::strspan<std::uint8_t>(response), socket.get())); - MDEBUG("Sent RPC reply: \"" << response << "\""); + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Sending RPC reply: \"" << response_view << "\""); + MONERO_UNWRAP(net::zmq::send(std::move(response), socket.get())); } } catch (const std::system_error& e) |