This commit is contained in:
53a45b2637452980 2024-04-22 10:26:40 -05:00 committed by GitHub
commit bd9fa79f15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 167 additions and 17 deletions

View File

@ -106,5 +106,7 @@ namespace math_helper
class once_a_time_milliseconds: public once_a_time<get_constant_interval<default_interval * (uint64_t)1000>, start_immediate> {};
template<typename get_interval, bool start_immediate = true>
class once_a_time_seconds_range: public once_a_time<get_interval, start_immediate> {};
template<typename get_interval, bool start_immediate = false>
class once_a_time_range: public once_a_time<get_interval, start_immediate> {};
}
}

View File

@ -220,6 +220,15 @@ namespace config
uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)2000000000); // 2 * pow(10, 9)
uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8)
// Both below are in nanoseconds. Onion connections are randomly dropped within this
// range and successive onion connections are delayed within this range
time_t const RANDOM_CONNECTION_DROP_LOWER = 2 * 60 * 1000000;
time_t const RANDOM_CONNECTION_DROP_UPPER = 4 * 60 * 1000000;
uint8_t const ACQUIRE_PEERS_RECONNECT_SCALING_FACTOR = 4;
// Onion circuit will expire after not being used for this many minutes
uint8_t const DEFAULT_ONION_CIRCUIT_EXPIRY = 10;
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18;
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19;
uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 42;

View File

@ -98,6 +98,16 @@ namespace levin
return std::chrono::steady_clock::duration{crypto::rand_range(rep(0), range.count())};
}
bool set_connection_can_broadcast_txs_to_peer(std::shared_ptr<connections> p2p, boost::uuids::uuid conn, bool val, bool exclusive_peers) {
if (exclusive_peers && !val)
return true;
return p2p->for_connection(conn, [&](detail::p2p_context& context)
{
context.can_broadcast_txs_to_peer = val;
return true;
});
}
uint64_t get_median_remote_height(connections& p2p)
{
std::vector<uint64_t> remote_heights;
@ -616,8 +626,9 @@ namespace levin
std::shared_ptr<detail::zone> zone_;
const std::size_t channel_;
const i_core_events* core_;
bool exclusive_peers_;
static void wait(const std::chrono::steady_clock::time_point start, std::shared_ptr<detail::zone> zone, const std::size_t index, const i_core_events* core)
static void wait(const std::chrono::steady_clock::time_point start, std::shared_ptr<detail::zone> zone, const std::size_t index, const i_core_events* core, bool exclusive_peers)
{
if (!zone)
return;
@ -625,7 +636,7 @@ namespace levin
noise_channel& channel = zone->channels.at(index);
channel.next_noise.expires_at(start + noise_min_delay + random_duration(noise_delay_range));
channel.next_noise.async_wait(
channel.strand.wrap(send_noise{std::move(zone), index, core})
channel.strand.wrap(send_noise{std::move(zone), index, core, exclusive_peers})
);
}
@ -643,12 +654,20 @@ namespace levin
const auto start = std::chrono::steady_clock::now();
noise_channel& channel = zone_->channels.at(channel_);
bool can_broadcast = true;
zone_->p2p->for_connection(channel.connection, [&](detail::p2p_context& context){
can_broadcast = context.can_broadcast_txs_to_peer;
return true;
});
if (!channel.connection.is_nil())
{
epee::byte_slice message = nullptr;
if (!channel.active.empty())
if (can_broadcast && !channel.active.empty())
{
message = channel.active.take_slice(zone_->noise.size());
else if (!channel.queue.empty())
}
else if (can_broadcast && !channel.queue.empty())
{
channel.active = channel.queue.front().clone();
message = channel.active.take_slice(zone_->noise.size());
@ -658,8 +677,12 @@ namespace levin
if (zone_->p2p->send(std::move(message), channel.connection))
{
if (!channel.queue.empty() && channel.active.empty())
if (can_broadcast && !channel.queue.empty() && channel.active.empty())
{
channel.queue.pop_front();
if (zone_->nzone == epee::net_utils::zone::tor)
set_connection_can_broadcast_txs_to_peer(zone_->p2p, channel.connection, false, exclusive_peers_);
}
}
else
{
@ -676,7 +699,7 @@ namespace levin
}
}
wait(start, std::move(zone_), channel_, core_);
wait(start, std::move(zone_), channel_, core_, exclusive_peers_);
}
};
@ -713,7 +736,7 @@ namespace levin
};
} // anonymous
notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, epee::net_utils::zone zone, const bool pad_txs, i_core_events& core)
notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, epee::net_utils::zone zone, const bool pad_txs, i_core_events& core, bool exclusive_peers)
: zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), zone, pad_txs))
, core_(std::addressof(core))
{
@ -731,7 +754,7 @@ namespace levin
start_epoch{zone_, min_epoch, epoch_range, out_count, core_}();
for (std::size_t channel = 0; channel < zone_->channels.size(); ++channel)
send_noise::wait(now, zone_, channel, core_);
send_noise::wait(now, zone_, channel, core_, exclusive_peers);
}
}

View File

@ -85,7 +85,7 @@ namespace levin
{}
//! Construct an instance with available notification `zones`.
explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, epee::net_utils::zone zone, bool pad_txs, i_core_events& core);
explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, epee::net_utils::zone zone, bool pad_txs, i_core_events& core, bool exclusive_peers);
notify(const notify&) = delete;
notify(notify&&) = default;

View File

@ -112,13 +112,15 @@ namespace nodetool
p2p_connection_context_t()
: peer_id(0),
support_flags(0),
m_in_timedsync(false)
m_in_timedsync(false),
can_broadcast_txs_to_peer(true)
{}
peerid_type peer_id;
uint32_t support_flags;
bool m_in_timedsync;
std::set<epee::net_utils::network_address> sent_addresses;
bool can_broadcast_txs_to_peer;
};
template<class t_payload_net_handler>
@ -146,12 +148,14 @@ namespace nodetool
config_t()
: m_net_config(),
m_peer_id(1),
m_support_flags(0)
m_support_flags(0),
can_broadcast_txs_to_peer(true)
{}
network_config m_net_config;
uint64_t m_peer_id;
uint32_t m_support_flags;
bool can_broadcast_txs_to_peer;
};
typedef epee::misc_utils::struct_init<config_t> config;
@ -255,7 +259,8 @@ namespace nodetool
is_closing(false),
m_network_id(),
m_enable_dns_seed_nodes(true),
max_connections(1)
max_connections(1),
m_make_next_tor_connection_after(0)
{}
virtual ~node_server();
@ -281,6 +286,8 @@ namespace nodetool
void get_public_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white);
void get_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white);
bool extend_make_next_connection_after(epee::net_utils::zone zone, bool acquire_peers);
void change_max_out_public_peers(size_t count);
uint32_t get_max_out_public_peers() const;
void change_max_in_public_peers(size_t count);
@ -419,6 +426,8 @@ namespace nodetool
bool gray_peerlist_housekeeping();
bool check_incoming_connections();
bool drop_random_outgoing_tor_connection();
void kill() { ///< will be called e.g. from deinit()
_info("Killing the net_node");
is_closing = true;
@ -474,6 +483,10 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
epee::math_helper::once_a_time_seconds<7000> m_dns_blocklist_interval;
template<uint64_t mini, uint64_t maxi> struct get_random_interval { public: uint64_t operator()() const { return crypto::rand_range(mini, maxi); } };
epee::math_helper::once_a_time_range<get_random_interval<::config::RANDOM_CONNECTION_DROP_LOWER, ::config::RANDOM_CONNECTION_DROP_UPPER>> m_drop_random_outgoing_tor_connection_interval;
time_t m_make_next_tor_connection_after;
std::list<epee::net_utils::network_address> m_priority_peers;
std::vector<epee::net_utils::network_address> m_exclusive_peers;
std::atomic_flag m_fallback_seed_nodes_added;
@ -516,6 +529,7 @@ namespace nodetool
bool m_enable_dns_blocklist;
uint32_t max_connections;
};
const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s

View File

@ -452,7 +452,7 @@ namespace nodetool
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
public_zone.m_notifier = cryptonote::levin::notify{
public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core()
public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core(), !m_exclusive_peers.empty()
};
if (command_line::has_arg(vm, arg_p2p_add_peer))
@ -605,7 +605,7 @@ namespace nodetool
}
zone.m_notifier = cryptonote::levin::notify{
zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core()
zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core(), !m_exclusive_peers.empty()
};
}
@ -1366,6 +1366,8 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp)
{
bool one_conn_below_max = false;
network_zone& zone = m_network_zones.at(na.get_zone());
if (zone.m_connect == nullptr) // outgoing connections in zone not possible
return false;
@ -1377,6 +1379,10 @@ namespace nodetool
{
return false;
}
else if (zone.m_current_number_of_out_peers == zone.m_config.m_net_config.max_out_connection_count - 1)
{
one_conn_below_max = true;
}
else if (zone.m_current_number_of_out_peers > zone.m_config.m_net_config.max_out_connection_count)
{
zone.m_net_server.get_config_object().del_out_connections(1);
@ -1384,6 +1390,9 @@ namespace nodetool
return false;
}
if (na.get_zone() == epee::net_utils::zone::tor && time(nullptr) < m_make_next_tor_connection_after) {
return false;
}
MDEBUG("Connecting to " << na.str() << "(peer_type=" << peer_type << ", last_seen: "
<< (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never")
@ -1441,6 +1450,17 @@ namespace nodetool
zone.m_notifier.on_handshake_complete(con->m_connection_id, con->m_is_income);
zone.m_notifier.new_out_connection();
if (na.get_zone() == epee::net_utils::zone::tor) {
if (one_conn_below_max)
{
extend_make_next_connection_after(na.get_zone(), false);
}
else
{
extend_make_next_connection_after(na.get_zone(), true);
}
}
LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK.");
return true;
}
@ -1804,6 +1824,35 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::extend_make_next_connection_after(epee::net_utils::zone zone,
bool acquire_peers) {
if (zone == epee::net_utils::zone::public_ || !m_exclusive_peers.empty())
return false;
auto scaling_factor = 1;
if (acquire_peers)
scaling_factor = ::config::ACQUIRE_PEERS_RECONNECT_SCALING_FACTOR;
auto offset = crypto::rand_range<time_t>(::config::RANDOM_CONNECTION_DROP_LOWER / scaling_factor,
::config::RANDOM_CONNECTION_DROP_UPPER / scaling_factor) / 1000000;
if (zone == epee::net_utils::zone::tor)
{
if (m_make_next_tor_connection_after > time(nullptr))
{
return false;
}
m_make_next_tor_connection_after = time(nullptr) + offset;
}
else if (zone == epee::net_utils::zone::i2p)
{
return false; // not implemented
}
return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::connections_maker()
{
using zone_type = epee::net_utils::zone;
@ -1816,6 +1865,10 @@ namespace nodetool
bool one_succeeded = false;
for(auto& zone : m_network_zones)
{
if (zone.first == zone_type::tor && m_make_next_tor_connection_after > time(nullptr)) {
continue;
}
size_t start_conn_count = get_outgoing_connections_count(zone.second);
if(!zone.second.m_peerlist.get_white_peers_count() && !connect_to_seed(zone.first))
{
@ -1827,7 +1880,6 @@ namespace nodetool
size_t base_expected_white_connections = (zone.second.m_config.m_net_config.max_out_connection_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100;
// carefully avoid `continue` in nested loop
size_t conn_count = get_outgoing_connections_count(zone.second);
while(conn_count < zone.second.m_config.m_net_config.max_out_connection_count)
{
@ -1865,12 +1917,25 @@ namespace nodetool
conn_count = new_conn_count;
}
if (start_conn_count == get_outgoing_connections_count(zone.second) && start_conn_count < zone.second.m_config.m_net_config.max_out_connection_count)
// max out connections set too high for available peers or connection issue
if (get_outgoing_connections_count(zone.second) <= start_conn_count && start_conn_count < zone.second.m_config.m_net_config.max_out_connection_count)
{
// add check if tor and not yet ready to connecct, if so avoid trying seeds.
MINFO("Failed to connect to any, trying seeds");
if (!connect_to_seed(zone.first))
{
// stop signal sent, extend as a precaution
extend_make_next_connection_after(zone.first, false);
continue;
}
}
// extend when no connection is made, prevents immediate new connection after peer drop
// acquire peers false because usually this will be max connections reached or set too high for available peers
// this will make a temporary connection issue worse
extend_make_next_connection_after(zone.first, false);
// this is really stop_signal_not_sent
one_succeeded = true;
}
@ -2017,6 +2082,43 @@ namespace nodetool
m_peerlist_store_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::store_config, this));
m_incoming_connections_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::check_incoming_connections, this));
m_dns_blocklist_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::update_dns_blocklist, this));
if (m_exclusive_peers.empty())
m_drop_random_outgoing_tor_connection_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::drop_random_outgoing_tor_connection, this));
return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::drop_random_outgoing_tor_connection()
{
std::vector<p2p_connection_context> outgoing_tor_connections;
auto tor_zone = m_network_zones.find(epee::net_utils::zone::tor);
if (tor_zone != m_network_zones.end()) {
tor_zone->second.m_net_server.get_config_object().foreach_connection([&](p2p_connection_context &cntx) {
if (!cntx.m_is_income) {
outgoing_tor_connections.push_back(cntx);
}
return true;
});
}
for (auto &cntx : outgoing_tor_connections) {
if (!cntx.can_broadcast_txs_to_peer) {
drop_connection(cntx);
// should use random auth for each connection but no socks5
// possible to use same circuit twice if drop initiated by remote
block_host(cntx.m_remote_address, (::config::DEFAULT_ONION_CIRCUIT_EXPIRY + 1) * 60, true);
return true;
}
}
if (auto len = outgoing_tor_connections.size()) {
auto index_to_drop = crypto::rand_idx(len);
drop_connection(outgoing_tor_connections[index_to_drop]);
block_host(outgoing_tor_connections[index_to_drop].m_remote_address,
(::config::DEFAULT_ONION_CIRCUIT_EXPIRY + 1) * 60, true);
}
return true;
}
//-----------------------------------------------------------------------------------

View File

@ -358,7 +358,7 @@ namespace
noise = epee::levin::make_noise_notify(noise_size);
epee::net_utils::zone zone = is_public ? epee::net_utils::zone::public_ : epee::net_utils::zone::i2p;
receiver_.notifier.reset(
new cryptonote::levin::notify{io_service_, connections_, std::move(noise), zone, pad_txs, events_}
new cryptonote::levin::notify{io_service_, connections_, std::move(noise), zone, pad_txs, events_, false}
);
return receiver_.notifier;
}