aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/CMakeLists.txt1
-rw-r--r--src/rpc/bootstrap_daemon.cpp40
-rw-r--r--src/rpc/bootstrap_daemon.h34
-rw-r--r--src/rpc/bootstrap_node_selector.cpp117
-rw-r--r--src/rpc/bootstrap_node_selector.h103
-rw-r--r--src/rpc/core_rpc_server.cpp81
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h2
-rw-r--r--src/rpc/daemon_handler.cpp29
-rw-r--r--src/rpc/daemon_handler.h5
-rw-r--r--src/rpc/message.cpp29
-rw-r--r--src/rpc/message.h11
-rw-r--r--src/rpc/rpc_args.cpp4
-rw-r--r--src/rpc/rpc_handler.h3
-rw-r--r--src/rpc/rpc_payment.cpp4
-rw-r--r--src/rpc/rpc_payment_signature.cpp5
-rw-r--r--src/rpc/zmq_server.cpp10
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)