diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index abfa665ff..8dbf1b53b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -109,6 +109,7 @@ #define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +#define P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT 2 #define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour #define P2P_IP_BLOCKTIME (60*60*24) //24 hour diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4582a3236..13cd3f5b0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -155,6 +155,8 @@ 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); @@ -205,15 +207,17 @@ namespace nodetool bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id); + bool make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist); bool make_new_connection_from_peerlist(bool use_white_list); - bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); + bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0); size_t get_random_index_with_fixed_probability(size_t max_index); bool is_peer_used(const peerlist_entry& peer); + bool is_peer_used(const anchor_peerlist_entry& peer); bool is_addr_connected(const net_address& peer); template bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool try_get_support_flags(const p2p_connection_context& context, std::function f); - bool make_expected_connections_count(bool white_list, size_t expected_connections); + bool make_expected_connections_count(PeerType peer_type, size_t expected_connections); void cache_connect_fail_info(const net_address& addr); bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 2e2dffab8..5c903b1f5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -874,6 +874,30 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::is_peer_used(const anchor_peerlist_entry& peer) + { + if(m_config.m_peer_id == peer.id) { + return true;//dont make connections to ourself + } + + bool used = false; + + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) + { + used = true; + + return false;//stop enumerating + } + + return true; + }); + + return used; + } + //----------------------------------------------------------------------------------- + template bool node_server::is_addr_connected(const net_address& peer) { bool connected = false; @@ -900,7 +924,7 @@ namespace nodetool } while(0) template - bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) + bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) { if (m_current_number_of_out_peers == m_config.m_net_config.connections_count) // out peers limit { @@ -913,7 +937,7 @@ namespace nodetool return false; } MDEBUG("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" - << epee::string_tools::num_to_string_fast(na.port) << "(white=" << white << ", last_seen: " + << epee::string_tools::num_to_string_fast(na.port) << "(peer_type=" << peer_type << ", last_seen: " << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << ")..."); @@ -963,6 +987,13 @@ namespace nodetool m_peerlist.append_with_peer_white(pe_local); //update last seen and push it to peerlist manager + anchor_peerlist_entry ape = AUTO_VAL_INIT(ape); + ape.adr = na; + ape.id = pi; + ape.first_seen = first_seen_stamp ? first_seen_stamp : time(nullptr); + + m_peerlist.append_with_peer_anchor(ape); + LOG_DEBUG_CC(con, "CONNECTION HANDSHAKED OK."); return true; } @@ -1029,6 +1060,41 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist) + { + for (const auto& pe: anchor_peerlist) { + _note("Considering connecting (out) to peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port)); + + if(is_peer_used(pe)) { + _note("Peer is used"); + continue; + } + + if(!is_remote_ip_allowed(pe.adr.ip)) { + continue; + } + + if(is_addr_recently_failed(pe.adr)) { + continue; + } + + MDEBUG("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) + << ":" << boost::lexical_cast(pe.adr.port) + << "[peer_type=" << anchor + << "] first_seen: " << epee::misc_utils::get_time_interval_string(time(NULL) - pe.first_seen)); + + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, 0, anchor, pe.first_seen)) { + _note("Handshake failed"); + continue; + } + + return true; + } + + return false; + } + //----------------------------------------------------------------------------------- + template bool node_server::make_new_connection_from_peerlist(bool use_white_list) { size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); @@ -1079,10 +1145,10 @@ namespace nodetool MDEBUG("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) - << "[white=" << use_white_list + << "[peer_list=" << (use_white_list ? white : gray) << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); - if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) { + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list ? white : gray)) { _note("Handshake failed"); continue; } @@ -1144,19 +1210,22 @@ namespace nodetool { if(conn_count < expected_white_connections) { - //start from white list - if(!make_expected_connections_count(true, expected_white_connections)) + //start from anchor list + if(!make_expected_connections_count(anchor, P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT)) return false; - //and then do grey list - if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + //then do white list + if(!make_expected_connections_count(white, expected_white_connections)) + return false; + //then do grey list + if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) return false; }else { //start from grey list - if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) return false; //and then do white list - if(!make_expected_connections_count(true, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(white, m_config.m_net_config.connections_count)) return false; } } @@ -1165,11 +1234,17 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::make_expected_connections_count(bool white_list, size_t expected_connections) + bool node_server::make_expected_connections_count(PeerType peer_type, size_t expected_connections) { if (m_offline) return true; + std::vector apl; + + if (peer_type == anchor) { + m_peerlist.get_and_empty_anchor_peerlist(apl); + } + size_t conn_count = get_outgoing_connections_count(); //add new connections from white peers while(conn_count < expected_connections) @@ -1177,8 +1252,18 @@ namespace nodetool if(m_net_server.is_stop_signal_sent()) return false; - if(!make_new_connection_from_peerlist(white_list)) + if (peer_type == anchor && !make_new_connection_from_anchor_peerlist(apl)) { break; + } + + if (peer_type == white && !make_new_connection_from_peerlist(true)) { + break; + } + + if (peer_type == gray && !make_new_connection_from_peerlist(false)) { + break; + } + conn_count = get_outgoing_connections_count(); } return true; @@ -1660,6 +1745,14 @@ namespace nodetool template void node_server::on_connection_close(p2p_connection_context& context) { + if (!m_net_server.is_stop_signal_sent() && !context.m_is_income) { + nodetool::net_address na = AUTO_VAL_INIT(na); + na.ip = context.m_remote_ip; + na.port = context.m_remote_port; + + m_peerlist.remove_from_peer_anchor(na); + } + MINFO("["<< epee::net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 11d995995..de0f51cc3 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -54,7 +54,7 @@ #include "net_peerlist_boost_serialization.h" -#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 4 +#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 5 namespace nodetool { @@ -77,13 +77,15 @@ namespace nodetool bool get_gray_peer_by_index(peerlist_entry& p, size_t i); bool append_with_peer_white(const peerlist_entry& pr); bool append_with_peer_gray(const peerlist_entry& pr); + bool append_with_peer_anchor(const anchor_peerlist_entry& ple); bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); bool set_peer_just_seen(peerid_type peer, const net_address& addr); bool set_peer_unreachable(const peerlist_entry& pr); bool is_ip_allowed(uint32_t ip); bool get_random_gray_peer(peerlist_entry& pe); bool remove_from_peer_gray(const peerlist_entry& pe); - + bool get_and_empty_anchor_peerlist(std::vector& apl); + bool remove_from_peer_anchor(const net_address& addr); private: struct by_time{}; @@ -145,6 +147,16 @@ namespace nodetool boost::multi_index::ordered_non_unique, boost::multi_index::member > > > peers_indexed_old; + + typedef boost::multi_index_container< + anchor_peerlist_entry, + boost::multi_index::indexed_by< + // access by anchor_peerlist_entry::net_adress + boost::multi_index::ordered_unique, boost::multi_index::member >, + // sort by anchor_peerlist_entry::first_seen + boost::multi_index::ordered_non_unique, boost::multi_index::member > + > + > anchor_peers_indexed; public: template @@ -161,8 +173,15 @@ namespace nodetool peers_indexed_from_old(pio, m_peers_white); return; } + a & m_peers_white; a & m_peers_gray; + + if(ver < 5) { + return; + } + + a & m_peers_anchor; } private: @@ -178,6 +197,7 @@ namespace nodetool peers_indexed m_peers_gray; peers_indexed m_peers_white; + anchor_peers_indexed m_peers_anchor; }; //-------------------------------------------------------------------------------------------------- inline @@ -398,6 +418,24 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline + bool peerlist_manager::append_with_peer_anchor(const anchor_peerlist_entry& ple) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + auto by_addr_it_anchor = m_peers_anchor.get().find(ple.adr); + + if(by_addr_it_anchor == m_peers_anchor.get().end()) { + m_peers_anchor.insert(ple); + } + + return true; + + CATCH_ENTRY_L0("peerlist_manager::append_with_peer_anchor()", false); + } + //-------------------------------------------------------------------------------------------------- + inline bool peerlist_manager::get_random_gray_peer(peerlist_entry& pe) { TRY_ENTRY(); @@ -435,7 +473,46 @@ namespace nodetool CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_gray()", false); } - //-------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_and_empty_anchor_peerlist(std::vector& apl) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + auto begin = m_peers_anchor.get().begin(); + auto end = m_peers_anchor.get().end(); + + std::for_each(begin, end, [&apl](const anchor_peerlist_entry &a) { + apl.push_back(a); + }); + + m_peers_anchor.get().clear(); + + return true; + + CATCH_ENTRY_L0("peerlist_manager::get_and_empty_anchor_peerlist()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::remove_from_peer_anchor(const net_address& addr) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + anchor_peers_indexed::index_iterator::type iterator = m_peers_anchor.get().find(addr); + + if (iterator != m_peers_anchor.get().end()) { + m_peers_anchor.erase(iterator); + } + + return true; + + CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_anchor()", false); + } + //-------------------------------------------------------------------------------------------------- } BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER) diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 260f16919..6891ac80c 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -49,6 +49,14 @@ namespace boost a & pl.adr; a & pl.id; a & pl.last_seen; - } + } + + template + inline void serialize(Archive &a, nodetool::anchor_peerlist_entry& pl, const ver_type ver) + { + a & pl.adr; + a & pl.id; + a & pl.first_seen; + } } } diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 97e75f5ca..5ec012714 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -56,6 +56,13 @@ namespace nodetool int64_t last_seen; }; + struct anchor_peerlist_entry + { + net_address adr; + peerid_type id; + int64_t first_seen; + }; + struct connection_entry { net_address adr;