Skip to content
Permalink
Browse files

daemon: automatic public nodes discovering and bootstrap daemon switc…

…hing
  • Loading branch information...
xiphon committed Aug 9, 2019
1 parent 1bb4ae3 commit a98117d28ce0810dfc9cf0e10d43f18e11f3698e
@@ -248,6 +248,8 @@ namespace nodetool
size_t get_public_white_peers_count();
size_t get_public_gray_peers_count();
void get_public_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white);
void get_public_nodes(PeerType type, std::vector<peerlist_entry>& public_nodes);
boost::optional<std::string> get_random_public_node();
size_t get_zone_count() const { return m_network_zones.size(); }

void change_max_out_public_peers(size_t count);
@@ -298,8 +300,6 @@ namespace nodetool
CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&)
END_INVOKE_MAP2()

enum PeerType { anchor = 0, white, gray };

//----------------- commands handlers ----------------------------------------------
int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context);
int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context);
@@ -1705,6 +1705,37 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
void node_server<t_payload_net_handler>::get_public_nodes(PeerType type, std::vector<peerlist_entry>& public_nodes)
{
auto public_zone = m_network_zones.find(epee::net_utils::zone::public_);
if (public_zone != m_network_zones.end())
public_zone->second.m_peerlist.get_public_nodes(type, public_nodes);
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_payload_net_handler>
boost::optional<std::string> node_server<t_payload_net_handler>::get_random_public_node()
{
std::vector<peerlist_entry> public_nodes;

get_public_nodes(PeerType::white, public_nodes);
if (public_nodes.empty())
{
MDEBUG("No white public node found, checking gray peers");

get_public_nodes(PeerType::gray, public_nodes);
if (public_nodes.empty())
{
MERROR("Failed to find any suitable public node");
return boost::none;
}
}

const auto& random_node = public_nodes[crypto::rand_idx(public_nodes.size())];
const auto address = random_node.adr.host_str() + ":" + std::to_string(random_node.rpc_port);
return address;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::idle_worker()
{
m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::peer_sync_idle_maker, this));
@@ -127,6 +127,12 @@ namespace nodetool
{
std::copy(src.begin(), src.end(), std::back_inserter(dest));
}

template<typename Container, typename Iterator>
void copy_peers(Container& dest, const std::pair<Iterator, Iterator>& src)
{
std::copy(std::get<0>(src), std::get<1>(src), std::back_inserter(dest));
}
} // anonymous

struct peerlist_join
@@ -277,6 +283,29 @@ namespace nodetool
copy_peers(pl_white, m_peers_white.get<by_addr>());
}

void peerlist_manager::get_public_nodes(PeerType type, std::vector<peerlist_entry>& public_nodes)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);

switch (type)
{
case PeerType::white:
{
copy_peers(public_nodes, m_peers_white.get<by_public_node>().equal_range(true));
break;
}
case PeerType::gray:
{
copy_peers(public_nodes, m_peers_gray.get<by_public_node>().equal_range(true));
break;
}
default:
{
break;
}
}
}

void peerlist_manager::get_peerlist(peerlist_types& peers)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
@@ -36,9 +36,11 @@
#include <vector>

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/optional/optional.hpp>
#include <boost/range/adaptor/reversed.hpp>

@@ -51,6 +53,8 @@

namespace nodetool
{
enum PeerType { anchor = 0, white, gray };

struct peerlist_types
{
std::vector<peerlist_entry> white;
@@ -105,6 +109,7 @@ namespace nodetool
bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
void get_peerlist(peerlist_types& peers);
void get_public_nodes(PeerType type, std::vector<peerlist_entry>& public_nodes);
bool get_white_peer_by_index(peerlist_entry& p, size_t i);
bool get_gray_peer_by_index(peerlist_entry& p, size_t i);
template<typename F> bool foreach(bool white, const F &f);
@@ -124,6 +129,7 @@ namespace nodetool
struct by_time{};
struct by_id{};
struct by_addr{};
struct by_public_node{};

struct modify_all_but_id
{
@@ -165,8 +171,9 @@ namespace nodetool
// access by peerlist_entry::net_adress
boost::multi_index::ordered_unique<boost::multi_index::tag<by_addr>, boost::multi_index::member<peerlist_entry,epee::net_utils::network_address,&peerlist_entry::adr> >,
// sort by peerlist_entry::last_seen<
boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,int64_t,&peerlist_entry::last_seen> >
>
boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_time>, boost::multi_index::member<peerlist_entry,int64_t,&peerlist_entry::last_seen> >,
boost::multi_index::hashed_non_unique<boost::multi_index::tag<by_public_node>, boost::multi_index::const_mem_fun<peerlist_entry, bool, &peerlist_entry::is_public_node>>
>
> peers_indexed;

typedef boost::multi_index_container<
@@ -78,6 +78,11 @@ namespace nodetool
uint32_t pruning_seed;
uint16_t rpc_port;

bool is_public_node() const
{
return rpc_port != 0;
}

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr)
KV_SERIALIZE(id)
@@ -30,6 +30,7 @@ set(rpc_base_sources
rpc_args.cpp)

set(rpc_sources
bootstrap_daemon.cpp
core_rpc_server.cpp
rpc_handler.cpp
instanciations)
@@ -53,6 +54,7 @@ set(daemon_rpc_server_headers)


set(rpc_daemon_private_headers
bootstrap_daemon.h
core_rpc_server.h
core_rpc_server_commands_defs.h
core_rpc_server_error_codes.h)
@@ -0,0 +1,95 @@
#include "bootstrap_daemon.h"

#include <stdexcept>

#include "crypto/crypto.h"
#include "cryptonote_core/cryptonote_core.h"
#include "misc_log_ex.h"

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon.bootstrap_daemon"

namespace cryptonote
{

bootstrap_daemon::bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node) noexcept
: m_get_next_public_node(get_next_public_node)
{
}

bootstrap_daemon::bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials)
: bootstrap_daemon(nullptr)
{
if (!set_server(address, credentials))
{
throw std::runtime_error("invalid bootstrap daemon address or credentials");
}
}

std::string bootstrap_daemon::address() const noexcept
{
const auto& host = m_http_client.get_host();
if (host.empty())
{
return std::string();
}
return host + ":" + m_http_client.get_port();
}

boost::optional<uint64_t> bootstrap_daemon::get_height()
{
cryptonote::COMMAND_RPC_GET_HEIGHT::request req;
cryptonote::COMMAND_RPC_GET_HEIGHT::response res;

if (!invoke_http_json("/getheight", req, res))
{
return boost::none;
}

if (res.status != CORE_RPC_STATUS_OK)
{
return boost::none;
}

return res.height;
}

bool bootstrap_daemon::handle_result(bool success)
{
if (!success && m_get_next_public_node)
{
m_http_client.disconnect();
}

return success;
}

bool bootstrap_daemon::set_server(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials /* = boost::none */)
{
if (!m_http_client.set_server(address, credentials))
{
MERROR("Failed to set bootstrap daemon address " << address);
return false;
}

MINFO("Changed bootstrap daemon address to " << address);
return true;
}


bool bootstrap_daemon::switch_server_if_needed()
{
if (!m_get_next_public_node || m_http_client.is_connected())
{
return true;
}

const boost::optional<std::string> address = m_get_next_public_node();
if (address) {
return set_server(*address);
}

return false;
}

}
@@ -0,0 +1,67 @@
#pragma once

#include <functional>
#include <vector>

#include <boost/optional/optional.hpp>
#include <boost/utility/string_ref.hpp>

#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"

namespace cryptonote
{

class bootstrap_daemon
{
public:
bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node) noexcept;
bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials);

std::string address() const noexcept;
boost::optional<uint64_t> get_height();
bool handle_result(bool success);

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)
{
if (!switch_server_if_needed())
{
return false;
}

return handle_result(epee::net_utils::invoke_http_json(uri, out_struct, result_struct, m_http_client));
}

template <class t_request, class t_response>
bool invoke_http_bin(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct)
{
if (!switch_server_if_needed())
{
return false;
}

return handle_result(epee::net_utils::invoke_http_bin(uri, out_struct, result_struct, m_http_client));
}

template <class t_request, class t_response>
bool invoke_http_json_rpc(const boost::string_ref command_name, const t_request &out_struct, t_response &result_struct)
{
if (!switch_server_if_needed())
{
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));
}

private:
bool set_server(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials = boost::none);
bool switch_server_if_needed();

private:
epee::net_utils::http::http_simple_client m_http_client;
std::function<boost::optional<std::string>()> m_get_next_public_node;
};

}

0 comments on commit a98117d

Please sign in to comment.
You can’t perform that action at this time.