Improve peer handling (#1796)
## Issue Addressed Potentially resolves #1647 and sync stalls. ## Proposed Changes The handling of the state of banned peers was inadequate for the complex peerdb data structure. We store a limited number of disconnected and banned peers in the db. We were not tracking intermediate "disconnecting" states and the in some circumstances we were updating the peer state without informing the peerdb. This lead to a number of inconsistencies in the peer state. Further, the peer manager could ban a peer changing a peer's state from being connected to banned. In this circumstance, if the peer then disconnected, we didn't inform the application layer, which lead to applications like sync not being informed of a peers disconnection. This could lead to sync stalling and having to require a lighthouse restart. Improved handling for peer states and interactions with the peerdb is made in this PR.
This commit is contained in:
parent
4298efeb23
commit
66f0cf4430
@ -851,44 +851,10 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
|
|||||||
self.peer_manager.addresses_of_peer(peer_id)
|
self.peer_manager.addresses_of_peer(peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This gets called every time a connection is closed.
|
|
||||||
fn inject_connection_closed(
|
|
||||||
&mut self,
|
|
||||||
peer_id: &PeerId,
|
|
||||||
conn_id: &ConnectionId,
|
|
||||||
endpoint: &ConnectedPoint,
|
|
||||||
) {
|
|
||||||
// If the peer manager (and therefore the behaviour's) believe this peer connected, inform
|
|
||||||
// about the disconnection.
|
|
||||||
if self.network_globals.peers.read().is_connected(&peer_id) {
|
|
||||||
delegate_to_behaviours!(self, inject_connection_closed, peer_id, conn_id, endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This gets called once there are no more active connections.
|
|
||||||
fn inject_disconnected(&mut self, peer_id: &PeerId) {
|
|
||||||
// If the application/behaviour layers thinks this peer has connected inform it of the disconnect.
|
|
||||||
if self.network_globals.peers.read().is_connected(&peer_id) {
|
|
||||||
// Inform the application.
|
|
||||||
self.add_event(BehaviourEvent::PeerDisconnected(peer_id.clone()));
|
|
||||||
// Inform the behaviour.
|
|
||||||
delegate_to_behaviours!(self, inject_disconnected, peer_id);
|
|
||||||
}
|
|
||||||
// Inform the peer manager.
|
|
||||||
// NOTE: It may be the case that a rejected node, due to too many peers is disconnected
|
|
||||||
// here and the peer manager has no knowledge of its connection. We insert it here for
|
|
||||||
// reference so that peer manager can track this peer.
|
|
||||||
self.peer_manager.notify_disconnect(&peer_id);
|
|
||||||
|
|
||||||
// Update the prometheus metrics
|
|
||||||
metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT);
|
|
||||||
metrics::set_gauge(
|
|
||||||
&metrics::PEERS_CONNECTED,
|
|
||||||
self.network_globals.connected_peers() as i64,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This gets called every time a connection is established.
|
// This gets called every time a connection is established.
|
||||||
|
// NOTE: The current logic implies that we would reject extra connections for already connected
|
||||||
|
// peers if we have reached our peer limit. This is fine for the time being as we currently
|
||||||
|
// only allow a single connection per peer.
|
||||||
fn inject_connection_established(
|
fn inject_connection_established(
|
||||||
&mut self,
|
&mut self,
|
||||||
peer_id: &PeerId,
|
peer_id: &PeerId,
|
||||||
@ -897,6 +863,9 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
|
|||||||
) {
|
) {
|
||||||
let goodbye_reason: Option<GoodbyeReason> = if self.peer_manager.is_banned(peer_id) {
|
let goodbye_reason: Option<GoodbyeReason> = if self.peer_manager.is_banned(peer_id) {
|
||||||
// If the peer is banned, send goodbye with reason banned.
|
// If the peer is banned, send goodbye with reason banned.
|
||||||
|
// A peer that has recently transitioned to the banned state should be in the
|
||||||
|
// disconnecting state, but the `is_banned()` function is dependent on score so should
|
||||||
|
// be true here in this case.
|
||||||
Some(GoodbyeReason::Banned)
|
Some(GoodbyeReason::Banned)
|
||||||
} else if self.peer_manager.peer_limit_reached()
|
} else if self.peer_manager.peer_limit_reached()
|
||||||
&& self
|
&& self
|
||||||
@ -913,14 +882,17 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if goodbye_reason.is_some() {
|
if let Some(goodbye_reason) = goodbye_reason {
|
||||||
debug!(self.log, "Disconnecting newly connected peer"; "peer_id" => peer_id.to_string(), "reason" => goodbye_reason.as_ref().expect("Is some").to_string());
|
debug!(self.log, "Disconnecting newly connected peer"; "peer_id" => peer_id.to_string(), "reason" => goodbye_reason.to_string());
|
||||||
self.peers_to_dc
|
self.peers_to_dc
|
||||||
.push_back((peer_id.clone(), goodbye_reason));
|
.push_back((peer_id.clone(), Some(goodbye_reason)));
|
||||||
|
// NOTE: We don't inform the peer manager that this peer is disconnecting. It is simply
|
||||||
|
// rejected with a goodbye.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify the peer manager of a successful connection
|
// All peers at this point will be registered as being connected.
|
||||||
|
// Notify the peer manager of a successful connection
|
||||||
match endpoint {
|
match endpoint {
|
||||||
ConnectedPoint::Listener { send_back_addr, .. } => {
|
ConnectedPoint::Listener { send_back_addr, .. } => {
|
||||||
self.peer_manager
|
self.peer_manager
|
||||||
@ -946,6 +918,8 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This gets called on the initial connection establishment.
|
// This gets called on the initial connection establishment.
|
||||||
|
// NOTE: This gets called after inject_connection_established. Therefore the logic in that
|
||||||
|
// function dictates the logic here.
|
||||||
fn inject_connected(&mut self, peer_id: &PeerId) {
|
fn inject_connected(&mut self, peer_id: &PeerId) {
|
||||||
// If the PeerManager has connected this peer, inform the behaviours
|
// If the PeerManager has connected this peer, inform the behaviours
|
||||||
if !self.network_globals.peers.read().is_connected(&peer_id) {
|
if !self.network_globals.peers.read().is_connected(&peer_id) {
|
||||||
@ -962,6 +936,79 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
|
|||||||
delegate_to_behaviours!(self, inject_connected, peer_id);
|
delegate_to_behaviours!(self, inject_connected, peer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This gets called every time a connection is closed.
|
||||||
|
// NOTE: The peer manager state can be modified in the lifetime of the peer. Due to the scoring
|
||||||
|
// mechanism. Peers can become banned. In this case, we still want to inform the behaviours.
|
||||||
|
fn inject_connection_closed(
|
||||||
|
&mut self,
|
||||||
|
peer_id: &PeerId,
|
||||||
|
conn_id: &ConnectionId,
|
||||||
|
endpoint: &ConnectedPoint,
|
||||||
|
) {
|
||||||
|
// If the peer manager (and therefore the behaviour's) believe this peer connected, inform
|
||||||
|
// about the disconnection.
|
||||||
|
// It could be the peer was in the process of being disconnected. In this case the
|
||||||
|
// sub-behaviours are expecting this peer to be connected and we inform them.
|
||||||
|
if self
|
||||||
|
.network_globals
|
||||||
|
.peers
|
||||||
|
.read()
|
||||||
|
.is_connected_or_disconnecting(peer_id)
|
||||||
|
{
|
||||||
|
// We are disconnecting the peer or the peer has already been connected.
|
||||||
|
// Both these cases, the peer has been previously registered in the sub protocols.
|
||||||
|
delegate_to_behaviours!(self, inject_connection_closed, peer_id, conn_id, endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gets called once there are no more active connections.
|
||||||
|
fn inject_disconnected(&mut self, peer_id: &PeerId) {
|
||||||
|
// If the application/behaviour layers thinks this peer has connected inform it of the disconnect.
|
||||||
|
|
||||||
|
if self
|
||||||
|
.network_globals
|
||||||
|
.peers
|
||||||
|
.read()
|
||||||
|
.is_connected_or_disconnecting(peer_id)
|
||||||
|
{
|
||||||
|
// We are disconnecting the peer or the peer has already been connected.
|
||||||
|
// Both these cases, the peer has been previously registered in the sub protocols and
|
||||||
|
// potentially the application layer.
|
||||||
|
// Inform the application.
|
||||||
|
self.add_event(BehaviourEvent::PeerDisconnected(peer_id.clone()));
|
||||||
|
// Inform the behaviour.
|
||||||
|
delegate_to_behaviours!(self, inject_disconnected, peer_id);
|
||||||
|
|
||||||
|
// Decrement the PEERS_PER_CLIENT metric
|
||||||
|
if let Some(kind) = self
|
||||||
|
.network_globals
|
||||||
|
.peers
|
||||||
|
.read()
|
||||||
|
.peer_info(peer_id)
|
||||||
|
.map(|info| info.client.kind.clone())
|
||||||
|
{
|
||||||
|
if let Some(v) =
|
||||||
|
metrics::get_int_gauge(&metrics::PEERS_PER_CLIENT, &[&kind.to_string()])
|
||||||
|
{
|
||||||
|
v.dec()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inform the peer manager.
|
||||||
|
// NOTE: It may be the case that a rejected node, due to too many peers is disconnected
|
||||||
|
// here and the peer manager has no knowledge of its connection. We insert it here for
|
||||||
|
// reference so that peer manager can track this peer.
|
||||||
|
self.peer_manager.notify_disconnect(&peer_id);
|
||||||
|
|
||||||
|
// Update the prometheus metrics
|
||||||
|
metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT);
|
||||||
|
metrics::set_gauge(
|
||||||
|
&metrics::PEERS_CONNECTED,
|
||||||
|
self.network_globals.connected_peers() as i64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn inject_addr_reach_failure(
|
fn inject_addr_reach_failure(
|
||||||
&mut self,
|
&mut self,
|
||||||
peer_id: Option<&PeerId>,
|
peer_id: Option<&PeerId>,
|
||||||
|
@ -136,65 +136,71 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
debug!(self.log, "Sending goodbye to peer"; "peer_id" => peer_id.to_string(), "reason" => reason.to_string(), "score" => info.score().to_string());
|
debug!(self.log, "Sending goodbye to peer"; "peer_id" => peer_id.to_string(), "reason" => reason.to_string(), "score" => info.score().to_string());
|
||||||
// Goodbye's are fatal
|
// Goodbye's are fatal
|
||||||
info.apply_peer_action_to_score(PeerAction::Fatal);
|
info.apply_peer_action_to_score(PeerAction::Fatal);
|
||||||
if info.connection_status.is_connected_or_dialing() {
|
}
|
||||||
|
|
||||||
|
// Update the peerdb and peer state accordingly
|
||||||
|
if self
|
||||||
|
.network_globals
|
||||||
|
.peers
|
||||||
|
.write()
|
||||||
|
.disconnect_and_ban(peer_id)
|
||||||
|
{
|
||||||
|
// update the state of the peer.
|
||||||
self.events
|
self.events
|
||||||
.push(PeerManagerEvent::DisconnectPeer(peer_id.clone(), reason));
|
.push(PeerManagerEvent::DisconnectPeer(peer_id.clone(), reason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Reports a peer for some action.
|
/// Reports a peer for some action.
|
||||||
///
|
///
|
||||||
/// If the peer doesn't exist, log a warning and insert defaults.
|
/// If the peer doesn't exist, log a warning and insert defaults.
|
||||||
pub fn report_peer(&mut self, peer_id: &PeerId, action: PeerAction) {
|
pub fn report_peer(&mut self, peer_id: &PeerId, action: PeerAction) {
|
||||||
// NOTE: This is duplicated in the update_peer_scores() and could be improved.
|
// Helper function to avoid any potential deadlocks.
|
||||||
|
|
||||||
// Variables to update the PeerDb if required.
|
|
||||||
let mut ban_peer = None;
|
let mut ban_peer = None;
|
||||||
let mut unban_peer = None;
|
let mut unban_peer = None;
|
||||||
|
|
||||||
if let Some(info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
|
{
|
||||||
|
let mut peer_db = self.network_globals.peers.write();
|
||||||
|
if let Some(info) = peer_db.peer_info_mut(peer_id) {
|
||||||
let previous_state = info.score_state();
|
let previous_state = info.score_state();
|
||||||
info.apply_peer_action_to_score(action);
|
info.apply_peer_action_to_score(action);
|
||||||
if previous_state != info.score_state() {
|
if previous_state != info.score_state() {
|
||||||
match info.score_state() {
|
match info.score_state() {
|
||||||
ScoreState::Banned => {
|
ScoreState::Banned => {
|
||||||
debug!(self.log, "Peer has been banned"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string());
|
debug!(self.log, "Peer has been banned"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string());
|
||||||
ban_peer = Some(peer_id.clone());
|
ban_peer = Some(peer_id);
|
||||||
if info.connection_status.is_connected_or_dialing() {
|
|
||||||
self.events.push(PeerManagerEvent::DisconnectPeer(
|
|
||||||
peer_id.clone(),
|
|
||||||
GoodbyeReason::BadScore,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ScoreState::Disconnected => {
|
ScoreState::Disconnected => {
|
||||||
debug!(self.log, "Peer transitioned to disconnect state"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string(), "past_state" => previous_state.to_string());
|
debug!(self.log, "Peer transitioned to disconnect state"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string(), "past_state" => previous_state.to_string());
|
||||||
// disconnect the peer if it's currently connected or dialing
|
// disconnect the peer if it's currently connected or dialing
|
||||||
unban_peer = Some(peer_id.clone());
|
if info.is_connected_or_dialing() {
|
||||||
if info.connection_status.is_connected_or_dialing() {
|
|
||||||
self.events.push(PeerManagerEvent::DisconnectPeer(
|
self.events.push(PeerManagerEvent::DisconnectPeer(
|
||||||
peer_id.clone(),
|
peer_id.clone(),
|
||||||
GoodbyeReason::BadScore,
|
GoodbyeReason::BadScore,
|
||||||
));
|
));
|
||||||
|
peer_db.notify_disconnecting(peer_id);
|
||||||
}
|
}
|
||||||
|
unban_peer = Some(peer_id);
|
||||||
}
|
}
|
||||||
ScoreState::Healthy => {
|
ScoreState::Healthy => {
|
||||||
debug!(self.log, "Peer transitioned to healthy state"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string(), "past_state" => previous_state.to_string());
|
debug!(self.log, "Peer transitioned to healthy state"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string(), "past_state" => previous_state.to_string());
|
||||||
// unban the peer if it was previously banned.
|
// unban the peer if it was previously banned.
|
||||||
unban_peer = Some(peer_id.clone());
|
unban_peer = Some(peer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!(self.log, "Peer score adjusted"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string());
|
debug!(self.log, "Peer score adjusted"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // end write lock
|
||||||
|
|
||||||
// Update the PeerDB state.
|
if let Some(peer_id) = ban_peer {
|
||||||
if let Some(peer_id) = ban_peer.take() {
|
self.ban_peer(peer_id);
|
||||||
self.ban_peer(&peer_id);
|
}
|
||||||
} else if let Some(peer_id) = unban_peer.take() {
|
if let Some(peer_id) = unban_peer {
|
||||||
self.unban_peer(&peer_id);
|
if let Err(e) = self.unban_peer(peer_id) {
|
||||||
|
error!(self.log, "{}", e; "peer_id" => %peer_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,37 +272,14 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
///
|
///
|
||||||
/// This is also called when dialing a peer fails.
|
/// This is also called when dialing a peer fails.
|
||||||
pub fn notify_disconnect(&mut self, peer_id: &PeerId) {
|
pub fn notify_disconnect(&mut self, peer_id: &PeerId) {
|
||||||
// Decrement the PEERS_PER_CLIENT metric
|
self.network_globals
|
||||||
if let Some(kind) = self
|
|
||||||
.network_globals
|
|
||||||
.peers
|
.peers
|
||||||
.read()
|
.write()
|
||||||
.peer_info(peer_id)
|
.notify_disconnect(peer_id);
|
||||||
.and_then(|peer_info| {
|
|
||||||
if let Connected { .. } = peer_info.connection_status {
|
|
||||||
Some(peer_info.client.kind.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
if let Some(v) =
|
|
||||||
metrics::get_int_gauge(&metrics::PEERS_PER_CLIENT, &[&kind.to_string()])
|
|
||||||
{
|
|
||||||
v.dec()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
self.network_globals.peers.write().disconnect(peer_id);
|
|
||||||
|
|
||||||
// remove the ping and status timer for the peer
|
// remove the ping and status timer for the peer
|
||||||
self.ping_peers.remove(peer_id);
|
self.ping_peers.remove(peer_id);
|
||||||
self.status_peers.remove(peer_id);
|
self.status_peers.remove(peer_id);
|
||||||
metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT);
|
|
||||||
metrics::set_gauge(
|
|
||||||
&metrics::PEERS_CONNECTED,
|
|
||||||
self.network_globals.connected_peers() as i64,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dial attempt has failed.
|
/// A dial attempt has failed.
|
||||||
@ -618,7 +601,7 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
fn connect_peer(&mut self, peer_id: &PeerId, connection: ConnectingType) -> bool {
|
fn connect_peer(&mut self, peer_id: &PeerId, connection: ConnectingType) -> bool {
|
||||||
{
|
{
|
||||||
let mut peerdb = self.network_globals.peers.write();
|
let mut peerdb = self.network_globals.peers.write();
|
||||||
if peerdb.connection_status(peer_id).map(|c| c.is_banned()) == Some(true) {
|
if peerdb.is_banned(&peer_id) {
|
||||||
// don't connect if the peer is banned
|
// don't connect if the peer is banned
|
||||||
slog::crit!(self.log, "Connection has been allowed to a banned peer"; "peer_id" => peer_id.to_string());
|
slog::crit!(self.log, "Connection has been allowed to a banned peer"; "peer_id" => peer_id.to_string());
|
||||||
}
|
}
|
||||||
@ -688,18 +671,14 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
ScoreState::Banned => {
|
ScoreState::Banned => {
|
||||||
debug!(self.log, "Peer has been banned"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string());
|
debug!(self.log, "Peer has been banned"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string());
|
||||||
to_ban_peers.push(peer_id.clone());
|
to_ban_peers.push(peer_id.clone());
|
||||||
if info.connection_status.is_connected_or_dialing() {
|
|
||||||
self.events.push(PeerManagerEvent::DisconnectPeer(
|
|
||||||
peer_id.clone(),
|
|
||||||
GoodbyeReason::BadScore,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ScoreState::Disconnected => {
|
ScoreState::Disconnected => {
|
||||||
debug!(self.log, "Peer transitioned to disconnect state"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string(), "past_state" => previous_state.to_string());
|
debug!(self.log, "Peer transitioned to disconnect state"; "peer_id" => peer_id.to_string(), "score" => info.score().to_string(), "past_state" => previous_state.to_string());
|
||||||
// disconnect the peer if it's currently connected or dialing
|
// disconnect the peer if it's currently connected or dialing
|
||||||
to_unban_peers.push(peer_id.clone());
|
to_unban_peers.push(peer_id.clone());
|
||||||
if info.connection_status.is_connected_or_dialing() {
|
if info.is_connected_or_dialing() {
|
||||||
|
// Change the state to inform that we are disconnecting the peer.
|
||||||
|
info.disconnecting(false);
|
||||||
self.events.push(PeerManagerEvent::DisconnectPeer(
|
self.events.push(PeerManagerEvent::DisconnectPeer(
|
||||||
peer_id.clone(),
|
peer_id.clone(),
|
||||||
GoodbyeReason::BadScore,
|
GoodbyeReason::BadScore,
|
||||||
@ -720,7 +699,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
}
|
}
|
||||||
// process unbanning peers
|
// process unbanning peers
|
||||||
for peer_id in to_unban_peers {
|
for peer_id in to_unban_peers {
|
||||||
self.unban_peer(&peer_id);
|
if let Err(e) = self.unban_peer(&peer_id) {
|
||||||
|
error!(self.log, "{}", e; "peer_id" => %peer_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,12 +710,26 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
/// Records updates the peers connection status and updates the peer db as well as blocks the
|
/// Records updates the peers connection status and updates the peer db as well as blocks the
|
||||||
/// peer from participating in discovery and removes them from the routing table.
|
/// peer from participating in discovery and removes them from the routing table.
|
||||||
fn ban_peer(&mut self, peer_id: &PeerId) {
|
fn ban_peer(&mut self, peer_id: &PeerId) {
|
||||||
|
{
|
||||||
|
// write lock scope
|
||||||
let mut peer_db = self.network_globals.peers.write();
|
let mut peer_db = self.network_globals.peers.write();
|
||||||
peer_db.ban(peer_id);
|
|
||||||
|
if peer_db.disconnect_and_ban(peer_id) {
|
||||||
|
// The peer was currently connected, so we start a disconnection.
|
||||||
|
self.events.push(PeerManagerEvent::DisconnectPeer(
|
||||||
|
peer_id.clone(),
|
||||||
|
GoodbyeReason::BadScore,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} // end write lock
|
||||||
|
|
||||||
|
// take a read lock
|
||||||
|
let peer_db = self.network_globals.peers.read();
|
||||||
|
|
||||||
let banned_ip_addresses = peer_db
|
let banned_ip_addresses = peer_db
|
||||||
.peer_info(peer_id)
|
.peer_info(peer_id)
|
||||||
.map(|info| {
|
.map(|info| {
|
||||||
info.seen_addresses
|
info.seen_addresses()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|ip| peer_db.is_ip_banned(ip))
|
.filter(|ip| peer_db.is_ip_banned(ip))
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -749,16 +744,17 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
///
|
///
|
||||||
/// Records updates the peers connection status and updates the peer db as well as removes
|
/// Records updates the peers connection status and updates the peer db as well as removes
|
||||||
/// previous bans from discovery.
|
/// previous bans from discovery.
|
||||||
fn unban_peer(&mut self, peer_id: &PeerId) {
|
fn unban_peer(&mut self, peer_id: &PeerId) -> Result<(), &'static str> {
|
||||||
let mut peer_db = self.network_globals.peers.write();
|
let mut peer_db = self.network_globals.peers.write();
|
||||||
peer_db.unban(&peer_id);
|
peer_db.unban(&peer_id)?;
|
||||||
|
|
||||||
let seen_ip_addresses = peer_db
|
let seen_ip_addresses = peer_db
|
||||||
.peer_info(peer_id)
|
.peer_info(peer_id)
|
||||||
.map(|info| info.seen_addresses.iter().cloned().collect::<Vec<_>>())
|
.map(|info| info.seen_addresses().iter().cloned().collect::<Vec<_>>())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
self.discovery.unban_peer(&peer_id, seen_ip_addresses);
|
self.discovery.unban_peer(&peer_id, seen_ip_addresses);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Peer manager's heartbeat maintains the peer count and maintains peer reputations.
|
/// The Peer manager's heartbeat maintains the peer count and maintains peer reputations.
|
||||||
@ -778,6 +774,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
// Updates peer's scores.
|
// Updates peer's scores.
|
||||||
self.update_peer_scores();
|
self.update_peer_scores();
|
||||||
|
|
||||||
|
// Keep a list of peers we are disconnecting
|
||||||
|
let mut disconnecting_peers = Vec::new();
|
||||||
|
|
||||||
let connected_peer_count = self.network_globals.connected_peers();
|
let connected_peer_count = self.network_globals.connected_peers();
|
||||||
if connected_peer_count > self.target_peers {
|
if connected_peer_count > self.target_peers {
|
||||||
//remove excess peers with the worst scores, but keep subnet peers
|
//remove excess peers with the worst scores, but keep subnet peers
|
||||||
@ -793,14 +792,20 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
|
|||||||
//disconnected in update_peer_scores
|
//disconnected in update_peer_scores
|
||||||
.filter(|(_, info)| info.score_state() == ScoreState::Healthy)
|
.filter(|(_, info)| info.score_state() == ScoreState::Healthy)
|
||||||
{
|
{
|
||||||
|
disconnecting_peers.push((*peer_id).clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut peer_db = self.network_globals.peers.write();
|
||||||
|
for peer_id in disconnecting_peers {
|
||||||
|
peer_db.notify_disconnecting(&peer_id);
|
||||||
self.events.push(PeerManagerEvent::DisconnectPeer(
|
self.events.push(PeerManagerEvent::DisconnectPeer(
|
||||||
(*peer_id).clone(),
|
peer_id,
|
||||||
GoodbyeReason::TooManyPeers,
|
GoodbyeReason::TooManyPeers,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<TSpec: EthSpec> Stream for PeerManager<TSpec> {
|
impl<TSpec: EthSpec> Stream for PeerManager<TSpec> {
|
||||||
type Item = PeerManagerEvent;
|
type Item = PeerManagerEvent;
|
||||||
|
@ -25,13 +25,13 @@ pub struct PeerInfo<T: EthSpec> {
|
|||||||
/// Client managing this peer
|
/// Client managing this peer
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
/// Connection status of this peer
|
/// Connection status of this peer
|
||||||
pub connection_status: PeerConnectionStatus,
|
connection_status: PeerConnectionStatus,
|
||||||
/// The known listening addresses of this peer. This is given by identify and can be arbitrary
|
/// The known listening addresses of this peer. This is given by identify and can be arbitrary
|
||||||
/// (including local IPs).
|
/// (including local IPs).
|
||||||
pub listening_addresses: Vec<Multiaddr>,
|
pub listening_addresses: Vec<Multiaddr>,
|
||||||
/// This is addresses we have physically seen and this is what we use for banning/un-banning
|
/// This is addresses we have physically seen and this is what we use for banning/un-banning
|
||||||
/// peers.
|
/// peers.
|
||||||
pub seen_addresses: HashSet<IpAddr>,
|
seen_addresses: HashSet<IpAddr>,
|
||||||
/// The current syncing state of the peer. The state may be determined after it's initial
|
/// The current syncing state of the peer. The state may be determined after it's initial
|
||||||
/// connection.
|
/// connection.
|
||||||
pub sync_status: PeerSyncStatus,
|
pub sync_status: PeerSyncStatus,
|
||||||
@ -91,6 +91,16 @@ impl<T: EthSpec> PeerInfo<T> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the seen addresses of the peer.
|
||||||
|
pub fn seen_addresses(&self) -> &HashSet<IpAddr> {
|
||||||
|
&self.seen_addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the connection status of the peer.
|
||||||
|
pub fn connection_status(&self) -> &PeerConnectionStatus {
|
||||||
|
&self.connection_status
|
||||||
|
}
|
||||||
|
|
||||||
/// Reports if this peer has some future validator duty in which case it is valuable to keep it.
|
/// Reports if this peer has some future validator duty in which case it is valuable to keep it.
|
||||||
pub fn has_future_duty(&self) -> bool {
|
pub fn has_future_duty(&self) -> bool {
|
||||||
self.min_ttl.map_or(false, |i| i >= Instant::now())
|
self.min_ttl.map_or(false, |i| i >= Instant::now())
|
||||||
@ -120,35 +130,161 @@ impl<T: EthSpec> PeerInfo<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Resets the peers score.
|
||||||
|
pub fn reset_score(&mut self) {
|
||||||
|
self.score.test_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Peer connection status API */
|
||||||
|
|
||||||
|
/// Checks if the status is connected.
|
||||||
|
pub fn is_connected(&self) -> bool {
|
||||||
|
matches!(self.connection_status, PeerConnectionStatus::Connected { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the status is connected.
|
||||||
|
pub fn is_dialing(&self) -> bool {
|
||||||
|
matches!(self.connection_status, PeerConnectionStatus::Dialing { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The peer is either connected or in the process of being dialed.
|
||||||
|
pub fn is_connected_or_dialing(&self) -> bool {
|
||||||
|
self.is_connected() || self.is_dialing()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the status is banned.
|
||||||
|
pub fn is_banned(&self) -> bool {
|
||||||
|
matches!(self.connection_status, PeerConnectionStatus::Banned { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the status is disconnected.
|
||||||
|
pub fn is_disconnected(&self) -> bool {
|
||||||
|
matches!(self.connection_status, Disconnected { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of connections with this peer.
|
||||||
|
pub fn connections(&self) -> (u8, u8) {
|
||||||
|
match self.connection_status {
|
||||||
|
Connected { n_in, n_out } => (n_in, n_out),
|
||||||
|
_ => (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
|
||||||
|
/// Modifies the status to Disconnected and sets the last seen instant to now. Returns None if
|
||||||
|
/// no changes were made. Returns Some(bool) where the bool represents if peer became banned or
|
||||||
|
/// simply just disconnected.
|
||||||
|
pub fn notify_disconnect(&mut self) -> Option<bool> {
|
||||||
|
match self.connection_status {
|
||||||
|
Banned { .. } | Disconnected { .. } => None,
|
||||||
|
Disconnecting { to_ban } => {
|
||||||
|
// If we are disconnecting this peer in the process of banning, we now ban the
|
||||||
|
// peer.
|
||||||
|
if to_ban {
|
||||||
|
self.connection_status = Banned {
|
||||||
|
since: Instant::now(),
|
||||||
|
};
|
||||||
|
Some(true)
|
||||||
|
} else {
|
||||||
|
self.connection_status = Disconnected {
|
||||||
|
since: Instant::now(),
|
||||||
|
};
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Connected { .. } | Dialing { .. } | Unknown => {
|
||||||
|
self.connection_status = Disconnected {
|
||||||
|
since: Instant::now(),
|
||||||
|
};
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the we are currently disconnecting this peer, after which the peer will be
|
||||||
|
/// considered banned.
|
||||||
|
// This intermediate state is required to inform the network behaviours that the sub-protocols
|
||||||
|
// are aware this peer exists and it is in the process of being banned. Compared to nodes that
|
||||||
|
// try to connect to us and are already banned (sub protocols do not know of these peers).
|
||||||
|
pub fn disconnecting(&mut self, to_ban: bool) {
|
||||||
|
self.connection_status = Disconnecting { to_ban }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the status to Banned
|
||||||
|
pub fn ban(&mut self) {
|
||||||
|
self.connection_status = Banned {
|
||||||
|
since: Instant::now(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The score system has unbanned the peer. Update the connection status
|
||||||
|
pub fn unban(&mut self) {
|
||||||
|
if let PeerConnectionStatus::Banned { since, .. } = self.connection_status {
|
||||||
|
self.connection_status = PeerConnectionStatus::Disconnected { since };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the status to Dialing
|
||||||
|
/// Returns an error if the current state is unexpected.
|
||||||
|
pub(crate) fn dialing_peer(&mut self) -> Result<(), &'static str> {
|
||||||
|
match &mut self.connection_status {
|
||||||
|
Connected { .. } => return Err("Dialing connected peer"),
|
||||||
|
Dialing { .. } => return Err("Dialing an already dialing peer"),
|
||||||
|
Disconnecting { .. } => return Err("Dialing a disconnecting peer"),
|
||||||
|
Disconnected { .. } | Banned { .. } | Unknown => {}
|
||||||
|
}
|
||||||
|
self.connection_status = Dialing {
|
||||||
|
since: Instant::now(),
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Modifies the status to Connected and increases the number of ingoing
|
/// Modifies the status to Connected and increases the number of ingoing
|
||||||
/// connections by one
|
/// connections by one
|
||||||
pub(crate) fn connect_ingoing(&mut self) {
|
pub(crate) fn connect_ingoing(&mut self, seen_address: Option<IpAddr>) {
|
||||||
match &mut self.connection_status {
|
match &mut self.connection_status {
|
||||||
Connected { n_in, .. } => *n_in += 1,
|
Connected { n_in, .. } => *n_in += 1,
|
||||||
Disconnected { .. } | Banned { .. } | Dialing { .. } | Unknown => {
|
Disconnected { .. }
|
||||||
|
| Banned { .. }
|
||||||
|
| Dialing { .. }
|
||||||
|
| Disconnecting { .. }
|
||||||
|
| Unknown => {
|
||||||
self.connection_status = Connected { n_in: 1, n_out: 0 };
|
self.connection_status = Connected { n_in: 1, n_out: 0 };
|
||||||
self.connection_direction = Some(ConnectionDirection::Incoming);
|
self.connection_direction = Some(ConnectionDirection::Incoming);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ip_addr) = seen_address {
|
||||||
|
self.seen_addresses.insert(ip_addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies the status to Connected and increases the number of outgoing
|
/// Modifies the status to Connected and increases the number of outgoing
|
||||||
/// connections by one
|
/// connections by one
|
||||||
pub(crate) fn connect_outgoing(&mut self) {
|
pub(crate) fn connect_outgoing(&mut self, seen_address: Option<IpAddr>) {
|
||||||
match &mut self.connection_status {
|
match &mut self.connection_status {
|
||||||
Connected { n_out, .. } => *n_out += 1,
|
Connected { n_out, .. } => *n_out += 1,
|
||||||
Disconnected { .. } | Banned { .. } | Dialing { .. } | Unknown => {
|
Disconnected { .. }
|
||||||
|
| Banned { .. }
|
||||||
|
| Dialing { .. }
|
||||||
|
| Disconnecting { .. }
|
||||||
|
| Unknown => {
|
||||||
self.connection_status = Connected { n_in: 0, n_out: 1 };
|
self.connection_status = Connected { n_in: 0, n_out: 1 };
|
||||||
self.connection_direction = Some(ConnectionDirection::Outgoing);
|
self.connection_direction = Some(ConnectionDirection::Outgoing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(ip_addr) = seen_address {
|
||||||
|
self.seen_addresses.insert(ip_addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Add an f64 to a non-trusted peer's score abiding by the limits.
|
/// Add an f64 to a non-trusted peer's score abiding by the limits.
|
||||||
pub fn add_to_score(&mut self, score: f64) {
|
pub fn add_to_score(&mut self, score: f64) {
|
||||||
if !self.is_trusted {
|
if !self.is_trusted {
|
||||||
self.score.add(score)
|
self.score.test_add(score)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,17 +321,21 @@ pub enum PeerConnectionStatus {
|
|||||||
/// number of outgoing connections.
|
/// number of outgoing connections.
|
||||||
n_out: u8,
|
n_out: u8,
|
||||||
},
|
},
|
||||||
|
/// The peer is being disconnected.
|
||||||
|
Disconnecting {
|
||||||
|
// After the disconnection the peer will be considered banned.
|
||||||
|
to_ban: bool,
|
||||||
|
},
|
||||||
/// The peer has disconnected.
|
/// The peer has disconnected.
|
||||||
Disconnected {
|
Disconnected {
|
||||||
/// last time the peer was connected or discovered.
|
/// last time the peer was connected or discovered.
|
||||||
since: Instant,
|
since: Instant,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The peer has been banned and is disconnected.
|
/// The peer has been banned and is disconnected.
|
||||||
Banned {
|
Banned {
|
||||||
/// moment when the peer was banned.
|
/// moment when the peer was banned.
|
||||||
since: Instant,
|
since: Instant,
|
||||||
/// ip addresses this peer had a the moment of the ban
|
|
||||||
ip_addresses: Vec<IpAddr>,
|
|
||||||
},
|
},
|
||||||
/// We are currently dialing this peer.
|
/// We are currently dialing this peer.
|
||||||
Dialing {
|
Dialing {
|
||||||
@ -209,14 +349,20 @@ pub enum PeerConnectionStatus {
|
|||||||
/// Serialization for http requests.
|
/// Serialization for http requests.
|
||||||
impl Serialize for PeerConnectionStatus {
|
impl Serialize for PeerConnectionStatus {
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
let mut s = serializer.serialize_struct("connection_status", 5)?;
|
let mut s = serializer.serialize_struct("connection_status", 6)?;
|
||||||
match self {
|
match self {
|
||||||
Connected { n_in, n_out } => {
|
Connected { n_in, n_out } => {
|
||||||
s.serialize_field("status", "connected")?;
|
s.serialize_field("status", "connected")?;
|
||||||
s.serialize_field("connections_in", n_in)?;
|
s.serialize_field("connections_in", n_in)?;
|
||||||
s.serialize_field("connections_out", n_out)?;
|
s.serialize_field("connections_out", n_out)?;
|
||||||
s.serialize_field("last_seen", &0)?;
|
s.serialize_field("last_seen", &0)?;
|
||||||
s.serialize_field("banned_ips", &Vec::<IpAddr>::new())?;
|
s.end()
|
||||||
|
}
|
||||||
|
Disconnecting { .. } => {
|
||||||
|
s.serialize_field("status", "disconnecting")?;
|
||||||
|
s.serialize_field("connections_in", &0)?;
|
||||||
|
s.serialize_field("connections_out", &0)?;
|
||||||
|
s.serialize_field("last_seen", &0)?;
|
||||||
s.end()
|
s.end()
|
||||||
}
|
}
|
||||||
Disconnected { since } => {
|
Disconnected { since } => {
|
||||||
@ -227,15 +373,11 @@ impl Serialize for PeerConnectionStatus {
|
|||||||
s.serialize_field("banned_ips", &Vec::<IpAddr>::new())?;
|
s.serialize_field("banned_ips", &Vec::<IpAddr>::new())?;
|
||||||
s.end()
|
s.end()
|
||||||
}
|
}
|
||||||
Banned {
|
Banned { since } => {
|
||||||
since,
|
|
||||||
ip_addresses,
|
|
||||||
} => {
|
|
||||||
s.serialize_field("status", "banned")?;
|
s.serialize_field("status", "banned")?;
|
||||||
s.serialize_field("connections_in", &0)?;
|
s.serialize_field("connections_in", &0)?;
|
||||||
s.serialize_field("connections_out", &0)?;
|
s.serialize_field("connections_out", &0)?;
|
||||||
s.serialize_field("last_seen", &since.elapsed().as_secs())?;
|
s.serialize_field("last_seen", &since.elapsed().as_secs())?;
|
||||||
s.serialize_field("banned_ips", &ip_addresses)?;
|
|
||||||
s.end()
|
s.end()
|
||||||
}
|
}
|
||||||
Dialing { since } => {
|
Dialing { since } => {
|
||||||
@ -243,7 +385,6 @@ impl Serialize for PeerConnectionStatus {
|
|||||||
s.serialize_field("connections_in", &0)?;
|
s.serialize_field("connections_in", &0)?;
|
||||||
s.serialize_field("connections_out", &0)?;
|
s.serialize_field("connections_out", &0)?;
|
||||||
s.serialize_field("last_seen", &since.elapsed().as_secs())?;
|
s.serialize_field("last_seen", &since.elapsed().as_secs())?;
|
||||||
s.serialize_field("banned_ips", &Vec::<IpAddr>::new())?;
|
|
||||||
s.end()
|
s.end()
|
||||||
}
|
}
|
||||||
Unknown => {
|
Unknown => {
|
||||||
@ -251,7 +392,6 @@ impl Serialize for PeerConnectionStatus {
|
|||||||
s.serialize_field("connections_in", &0)?;
|
s.serialize_field("connections_in", &0)?;
|
||||||
s.serialize_field("connections_out", &0)?;
|
s.serialize_field("connections_out", &0)?;
|
||||||
s.serialize_field("last_seen", &0)?;
|
s.serialize_field("last_seen", &0)?;
|
||||||
s.serialize_field("banned_ips", &Vec::<IpAddr>::new())?;
|
|
||||||
s.end()
|
s.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,59 +403,3 @@ impl Default for PeerConnectionStatus {
|
|||||||
PeerConnectionStatus::Unknown
|
PeerConnectionStatus::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PeerConnectionStatus {
|
|
||||||
/// Checks if the status is connected.
|
|
||||||
pub fn is_connected(&self) -> bool {
|
|
||||||
matches!(self, PeerConnectionStatus::Connected { .. })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the status is connected.
|
|
||||||
pub fn is_dialing(&self) -> bool {
|
|
||||||
matches!(self, PeerConnectionStatus::Dialing { .. })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The peer is either connected or in the process of being dialed.
|
|
||||||
pub fn is_connected_or_dialing(&self) -> bool {
|
|
||||||
self.is_connected() || self.is_dialing()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the status is banned.
|
|
||||||
pub fn is_banned(&self) -> bool {
|
|
||||||
matches!(self, PeerConnectionStatus::Banned { .. })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the status is disconnected.
|
|
||||||
pub fn is_disconnected(&self) -> bool {
|
|
||||||
matches!(self, Disconnected { .. })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modifies the status to Disconnected and sets the last seen instant to now
|
|
||||||
pub fn disconnect(&mut self) {
|
|
||||||
*self = Disconnected {
|
|
||||||
since: Instant::now(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modifies the status to Banned
|
|
||||||
pub fn ban(&mut self, ip_addresses: Vec<IpAddr>) {
|
|
||||||
*self = Banned {
|
|
||||||
since: Instant::now(),
|
|
||||||
ip_addresses,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The score system has unbanned the peer. Update the connection status
|
|
||||||
pub fn unban(&mut self) {
|
|
||||||
if let PeerConnectionStatus::Banned { since, .. } = self {
|
|
||||||
*self = PeerConnectionStatus::Disconnected { since: *since }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connections(&self) -> (u8, u8) {
|
|
||||||
match self {
|
|
||||||
Connected { n_in, n_out } => (*n_in, *n_out),
|
|
||||||
_ => (0, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,8 +6,8 @@ use crate::rpc::methods::MetaData;
|
|||||||
use crate::Enr;
|
use crate::Enr;
|
||||||
use crate::PeerId;
|
use crate::PeerId;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use slog::{crit, debug, trace, warn};
|
use slog::{crit, debug, error, trace, warn};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use types::{EthSpec, SubnetId};
|
use types::{EthSpec, SubnetId};
|
||||||
@ -42,29 +42,21 @@ pub struct BannedPeersCount {
|
|||||||
impl BannedPeersCount {
|
impl BannedPeersCount {
|
||||||
/// Removes the peer from the counts if it is banned. Returns true if the peer was banned and
|
/// Removes the peer from the counts if it is banned. Returns true if the peer was banned and
|
||||||
/// false otherwise.
|
/// false otherwise.
|
||||||
pub fn remove_banned_peer(&mut self, connection_status: &PeerConnectionStatus) -> bool {
|
pub fn remove_banned_peer(&mut self, ip_addresses: &HashSet<IpAddr>) {
|
||||||
match connection_status {
|
|
||||||
PeerConnectionStatus::Banned { ip_addresses, .. } => {
|
|
||||||
self.banned_peers = self.banned_peers.saturating_sub(1);
|
self.banned_peers = self.banned_peers.saturating_sub(1);
|
||||||
for address in ip_addresses {
|
for address in ip_addresses {
|
||||||
if let Some(count) = self.banned_peers_per_ip.get_mut(address) {
|
if let Some(count) = self.banned_peers_per_ip.get_mut(address) {
|
||||||
*count = count.saturating_sub(1);
|
*count = count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false, //if not banned do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_banned_peer(&mut self, connection_status: &PeerConnectionStatus) {
|
pub fn add_banned_peer(&mut self, ip_addresses: &HashSet<IpAddr>) {
|
||||||
if let PeerConnectionStatus::Banned { ip_addresses, .. } = connection_status {
|
self.banned_peers = self.banned_peers.saturating_add(1);
|
||||||
self.banned_peers += 1;
|
|
||||||
for address in ip_addresses {
|
for address in ip_addresses {
|
||||||
*self.banned_peers_per_ip.entry(*address).or_insert(0) += 1;
|
*self.banned_peers_per_ip.entry(*address).or_insert(0) += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn banned_peers(&self) -> usize {
|
pub fn banned_peers(&self) -> usize {
|
||||||
self.banned_peers
|
self.banned_peers
|
||||||
@ -148,6 +140,13 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
matches!(self.connection_status(peer_id), Some(PeerConnectionStatus::Connected { .. })
|
matches!(self.connection_status(peer_id), Some(PeerConnectionStatus::Connected { .. })
|
||||||
| Some(PeerConnectionStatus::Dialing { .. }))
|
| Some(PeerConnectionStatus::Dialing { .. }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If we are connected or in the process of disconnecting
|
||||||
|
pub fn is_connected_or_disconnecting(&self, peer_id: &PeerId) -> bool {
|
||||||
|
matches!(self.connection_status(peer_id), Some(PeerConnectionStatus::Connected { .. })
|
||||||
|
| Some(PeerConnectionStatus::Disconnecting { .. }))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the peer is synced at least to our current head.
|
/// Returns true if the peer is synced at least to our current head.
|
||||||
pub fn is_synced(&self, peer_id: &PeerId) -> bool {
|
pub fn is_synced(&self, peer_id: &PeerId) -> bool {
|
||||||
match self.peers.get(peer_id).map(|info| &info.sync_status) {
|
match self.peers.get(peer_id).map(|info| &info.sync_status) {
|
||||||
@ -157,7 +156,11 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the Peer is banned.
|
/// Returns true if the Peer is banned. This doesn't check the connection state, rather the
|
||||||
|
/// underlying score of the peer. A peer may be banned but still in the connected state
|
||||||
|
/// temporarily.
|
||||||
|
///
|
||||||
|
/// This is used to determine if we should accept incoming connections or not.
|
||||||
pub fn is_banned(&self, peer_id: &PeerId) -> bool {
|
pub fn is_banned(&self, peer_id: &PeerId) -> bool {
|
||||||
if let Some(peer) = self.peers.get(peer_id) {
|
if let Some(peer) = self.peers.get(peer_id) {
|
||||||
match peer.score().state() {
|
match peer.score().state() {
|
||||||
@ -170,7 +173,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ip_is_banned(&self, peer: &PeerInfo<TSpec>) -> bool {
|
fn ip_is_banned(&self, peer: &PeerInfo<TSpec>) -> bool {
|
||||||
peer.seen_addresses
|
peer.seen_addresses()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|addr| self.banned_peers_count.ip_is_banned(addr))
|
.any(|addr| self.banned_peers_count.ip_is_banned(addr))
|
||||||
}
|
}
|
||||||
@ -194,16 +197,14 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
|
|
||||||
/// Gives the ids and info of all known connected peers.
|
/// Gives the ids and info of all known connected peers.
|
||||||
pub fn connected_peers(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<TSpec>)> {
|
pub fn connected_peers(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<TSpec>)> {
|
||||||
self.peers
|
self.peers.iter().filter(|(_, info)| info.is_connected())
|
||||||
.iter()
|
|
||||||
.filter(|(_, info)| info.connection_status.is_connected())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives the ids of all known connected peers.
|
/// Gives the ids of all known connected peers.
|
||||||
pub fn connected_peer_ids(&self) -> impl Iterator<Item = &PeerId> {
|
pub fn connected_peer_ids(&self) -> impl Iterator<Item = &PeerId> {
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| info.connection_status.is_connected())
|
.filter(|(_, info)| info.is_connected())
|
||||||
.map(|(peer_id, _)| peer_id)
|
.map(|(peer_id, _)| peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,9 +212,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
pub fn connected_or_dialing_peers(&self) -> impl Iterator<Item = &PeerId> {
|
pub fn connected_or_dialing_peers(&self) -> impl Iterator<Item = &PeerId> {
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| {
|
.filter(|(_, info)| info.is_connected() || info.is_dialing())
|
||||||
info.connection_status.is_connected() || info.connection_status.is_dialing()
|
|
||||||
})
|
|
||||||
.map(|(peer_id, _)| peer_id)
|
.map(|(peer_id, _)| peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +222,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| {
|
.filter(|(_, info)| {
|
||||||
if info.sync_status.is_synced() || info.sync_status.is_advanced() {
|
if info.sync_status.is_synced() || info.sync_status.is_advanced() {
|
||||||
return info.connection_status.is_connected();
|
return info.is_connected();
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
@ -236,7 +235,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| {
|
.filter(|(_, info)| {
|
||||||
if info.sync_status.is_advanced() {
|
if info.sync_status.is_advanced() {
|
||||||
return info.connection_status.is_connected();
|
return info.is_connected();
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
@ -247,9 +246,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
pub fn peers_on_subnet(&self, subnet_id: SubnetId) -> impl Iterator<Item = &PeerId> {
|
pub fn peers_on_subnet(&self, subnet_id: SubnetId) -> impl Iterator<Item = &PeerId> {
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(move |(_, info)| {
|
.filter(move |(_, info)| info.is_connected() && info.on_subnet(subnet_id))
|
||||||
info.connection_status.is_connected() && info.on_subnet(subnet_id)
|
|
||||||
})
|
|
||||||
.map(|(peer_id, _)| peer_id)
|
.map(|(peer_id, _)| peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +254,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
pub fn disconnected_peers(&self) -> impl Iterator<Item = &PeerId> {
|
pub fn disconnected_peers(&self) -> impl Iterator<Item = &PeerId> {
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| info.connection_status.is_disconnected())
|
.filter(|(_, info)| info.is_disconnected())
|
||||||
.map(|(peer_id, _)| peer_id)
|
.map(|(peer_id, _)| peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +262,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
pub fn banned_peers(&self) -> impl Iterator<Item = &PeerId> {
|
pub fn banned_peers(&self) -> impl Iterator<Item = &PeerId> {
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| info.connection_status.is_banned())
|
.filter(|(_, info)| info.is_banned())
|
||||||
.map(|(peer_id, _)| peer_id)
|
.map(|(peer_id, _)| peer_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +272,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
let mut connected = self
|
let mut connected = self
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| info.connection_status.is_connected())
|
.filter(|(_, info)| info.is_connected())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
connected.shuffle(&mut rand::thread_rng());
|
connected.shuffle(&mut rand::thread_rng());
|
||||||
@ -287,12 +284,12 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
/// score from highest to lowest, and filtered using `is_status`
|
/// score from highest to lowest, and filtered using `is_status`
|
||||||
pub fn best_peers_by_status<F>(&self, is_status: F) -> Vec<(&PeerId, &PeerInfo<TSpec>)>
|
pub fn best_peers_by_status<F>(&self, is_status: F) -> Vec<(&PeerId, &PeerInfo<TSpec>)>
|
||||||
where
|
where
|
||||||
F: Fn(&PeerConnectionStatus) -> bool,
|
F: Fn(&PeerInfo<TSpec>) -> bool,
|
||||||
{
|
{
|
||||||
let mut by_status = self
|
let mut by_status = self
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| is_status(&info.connection_status))
|
.filter(|(_, info)| is_status(&info))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
by_status.sort_by_key(|(_, info)| info.score());
|
by_status.sort_by_key(|(_, info)| info.score());
|
||||||
by_status.into_iter().rev().collect()
|
by_status.into_iter().rev().collect()
|
||||||
@ -301,11 +298,11 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
/// Returns the peer with highest reputation that satisfies `is_status`
|
/// Returns the peer with highest reputation that satisfies `is_status`
|
||||||
pub fn best_by_status<F>(&self, is_status: F) -> Option<&PeerId>
|
pub fn best_by_status<F>(&self, is_status: F) -> Option<&PeerId>
|
||||||
where
|
where
|
||||||
F: Fn(&PeerConnectionStatus) -> bool,
|
F: Fn(&PeerInfo<TSpec>) -> bool,
|
||||||
{
|
{
|
||||||
self.peers
|
self.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| is_status(&info.connection_status))
|
.filter(|(_, info)| is_status(&info))
|
||||||
.max_by_key(|(_, info)| info.score())
|
.max_by_key(|(_, info)| info.score())
|
||||||
.map(|(id, _)| id)
|
.map(|(id, _)| id)
|
||||||
}
|
}
|
||||||
@ -313,7 +310,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
/// Returns the peer's connection status. Returns unknown if the peer is not in the DB.
|
/// Returns the peer's connection status. Returns unknown if the peer is not in the DB.
|
||||||
pub fn connection_status(&self, peer_id: &PeerId) -> Option<PeerConnectionStatus> {
|
pub fn connection_status(&self, peer_id: &PeerId) -> Option<PeerConnectionStatus> {
|
||||||
self.peer_info(peer_id)
|
self.peer_info(peer_id)
|
||||||
.map(|info| info.connection_status.clone())
|
.map(|info| info.connection_status().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setters */
|
/* Setters */
|
||||||
@ -323,16 +320,18 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
let info = self.peers.entry(peer_id.clone()).or_default();
|
let info = self.peers.entry(peer_id.clone()).or_default();
|
||||||
info.enr = enr;
|
info.enr = enr;
|
||||||
|
|
||||||
if info.connection_status.is_disconnected() {
|
if info.is_disconnected() {
|
||||||
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info.is_banned() {
|
||||||
self.banned_peers_count
|
self.banned_peers_count
|
||||||
.remove_banned_peer(&info.connection_status);
|
.remove_banned_peer(info.seen_addresses());
|
||||||
|
}
|
||||||
|
|
||||||
info.connection_status = PeerConnectionStatus::Dialing {
|
if let Err(e) = info.dialing_peer() {
|
||||||
since: Instant::now(),
|
error!(self.log, "{}", e);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update min ttl of a peer.
|
/// Update min ttl of a peer.
|
||||||
@ -357,7 +356,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
let log = &self.log;
|
let log = &self.log;
|
||||||
self.peers.iter_mut()
|
self.peers.iter_mut()
|
||||||
.filter(move |(_, info)| {
|
.filter(move |(_, info)| {
|
||||||
info.connection_status.is_connected() && info.on_subnet(subnet_id)
|
info.is_connected() && info.on_subnet(subnet_id)
|
||||||
})
|
})
|
||||||
.for_each(|(peer_id,info)| {
|
.for_each(|(peer_id,info)| {
|
||||||
if info.min_ttl.is_none() || Some(min_ttl) > info.min_ttl {
|
if info.min_ttl.is_none() || Some(min_ttl) > info.min_ttl {
|
||||||
@ -367,7 +366,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
.checked_duration_since(Instant::now())
|
.checked_duration_since(Instant::now())
|
||||||
.map(|duration| duration.as_secs())
|
.map(|duration| duration.as_secs())
|
||||||
.unwrap_or_else(|| 0);
|
.unwrap_or_else(|| 0);
|
||||||
trace!(log, "Updating minimum duration a peer is required for"; "peer_id" => peer_id.to_string(), "min_ttl" => min_ttl_secs);
|
trace!(log, "Updating minimum duration a peer is required for"; "peer_id" => %peer_id, "min_ttl" => min_ttl_secs);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,21 +375,24 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
let info = self.peers.entry(peer_id.clone()).or_default();
|
let info = self.peers.entry(peer_id.clone()).or_default();
|
||||||
info.enr = enr;
|
info.enr = enr;
|
||||||
|
|
||||||
if info.connection_status.is_disconnected() {
|
if info.is_disconnected() {
|
||||||
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info.is_banned() {
|
||||||
|
error!(self.log, "Accepted a connection from a banned peer"; "peer_id" => %peer_id);
|
||||||
self.banned_peers_count
|
self.banned_peers_count
|
||||||
.remove_banned_peer(&info.connection_status);
|
.remove_banned_peer(info.seen_addresses());
|
||||||
info.connect_ingoing();
|
}
|
||||||
|
|
||||||
// Add the seen ip address to the peer's info
|
// Add the seen ip address to the peer's info
|
||||||
if let Some(ip_addr) = multiaddr.iter().find_map(|p| match p {
|
let ip_addr = multiaddr.iter().find_map(|p| match p {
|
||||||
Protocol::Ip4(ip) => Some(ip.into()),
|
Protocol::Ip4(ip) => Some(ip.into()),
|
||||||
Protocol::Ip6(ip) => Some(ip.into()),
|
Protocol::Ip6(ip) => Some(ip.into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}) {
|
});
|
||||||
info.seen_addresses.insert(ip_addr);
|
|
||||||
}
|
info.connect_ingoing(ip_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a peer as connected with an outgoing connection.
|
/// Sets a peer as connected with an outgoing connection.
|
||||||
@ -398,38 +400,56 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
let info = self.peers.entry(peer_id.clone()).or_default();
|
let info = self.peers.entry(peer_id.clone()).or_default();
|
||||||
info.enr = enr;
|
info.enr = enr;
|
||||||
|
|
||||||
if info.connection_status.is_disconnected() {
|
if info.is_disconnected() {
|
||||||
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info.is_banned() {
|
||||||
|
error!(self.log, "Connected to a banned peer"; "peer_id" => %peer_id);
|
||||||
self.banned_peers_count
|
self.banned_peers_count
|
||||||
.remove_banned_peer(&info.connection_status);
|
.remove_banned_peer(info.seen_addresses());
|
||||||
info.connect_outgoing();
|
}
|
||||||
|
|
||||||
// Add the seen ip address to the peer's info
|
// Add the seen ip address to the peer's info
|
||||||
if let Some(ip_addr) = multiaddr.iter().find_map(|p| match p {
|
let ip_addr = multiaddr.iter().find_map(|p| match p {
|
||||||
Protocol::Ip4(ip) => Some(ip.into()),
|
Protocol::Ip4(ip) => Some(ip.into()),
|
||||||
Protocol::Ip6(ip) => Some(ip.into()),
|
Protocol::Ip6(ip) => Some(ip.into()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}) {
|
});
|
||||||
info.seen_addresses.insert(ip_addr);
|
|
||||||
}
|
info.connect_outgoing(ip_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the peer as disconnected. A banned peer remains banned
|
/// Sets the peer as disconnected. A banned peer remains banned
|
||||||
pub fn disconnect(&mut self, peer_id: &PeerId) {
|
pub fn notify_disconnect(&mut self, peer_id: &PeerId) {
|
||||||
// Note that it could be the case we prevent new nodes from joining. In this instance,
|
// Note that it could be the case we prevent new nodes from joining. In this instance,
|
||||||
// we don't bother tracking the new node.
|
// we don't bother tracking the new node.
|
||||||
if let Some(info) = self.peers.get_mut(peer_id) {
|
if let Some(info) = self.peers.get_mut(peer_id) {
|
||||||
if !info.connection_status.is_disconnected() && !info.connection_status.is_banned() {
|
if let Some(became_banned) = info.notify_disconnect() {
|
||||||
info.connection_status.disconnect();
|
if became_banned {
|
||||||
|
self.banned_peers_count
|
||||||
|
.add_banned_peer(info.seen_addresses());
|
||||||
|
} else {
|
||||||
self.disconnected_peers += 1;
|
self.disconnected_peers += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.shrink_to_fit();
|
self.shrink_to_fit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks a peer as banned.
|
/// Notifies the peer manager that the peer is undergoing a normal disconnect (without banning
|
||||||
pub fn ban(&mut self, peer_id: &PeerId) {
|
/// afterwards.
|
||||||
|
pub fn notify_disconnecting(&mut self, peer_id: &PeerId) {
|
||||||
|
if let Some(info) = self.peers.get_mut(peer_id) {
|
||||||
|
info.disconnecting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks a peer to be disconnected and then banned.
|
||||||
|
/// Returns true if the peer is currently connected and false otherwise.
|
||||||
|
// NOTE: If the peer's score is not already low enough to be banned, this will decrease the
|
||||||
|
// peer's score to be a banned state.
|
||||||
|
pub fn disconnect_and_ban(&mut self, peer_id: &PeerId) -> bool {
|
||||||
let log_ref = &self.log;
|
let log_ref = &self.log;
|
||||||
let info = self.peers.entry(peer_id.clone()).or_insert_with(|| {
|
let info = self.peers.entry(peer_id.clone()).or_insert_with(|| {
|
||||||
warn!(log_ref, "Banning unknown peer";
|
warn!(log_ref, "Banning unknown peer";
|
||||||
@ -437,20 +457,56 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
PeerInfo::default()
|
PeerInfo::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
if info.connection_status.is_disconnected() {
|
// Ban the peer if the score is not already low enough.
|
||||||
|
match info.score().state() {
|
||||||
|
ScoreState::Banned => {}
|
||||||
|
_ => {
|
||||||
|
// If score isn't low enough to ban, this function has been called incorrectly.
|
||||||
|
error!(self.log, "Banning a peer with a good score"; "peer_id" => %peer_id);
|
||||||
|
info.apply_peer_action_to_score(super::score::PeerAction::Fatal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and verify all the connection states
|
||||||
|
match info.connection_status() {
|
||||||
|
PeerConnectionStatus::Disconnected { .. } => {
|
||||||
|
// It should not be possible to ban a peer that is already disconnected.
|
||||||
|
error!(log_ref, "Banning a disconnected peer"; "peer_id" => %peer_id);
|
||||||
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
self.disconnected_peers = self.disconnected_peers.saturating_sub(1);
|
||||||
}
|
info.ban();
|
||||||
if !info.connection_status.is_banned() {
|
|
||||||
info.connection_status
|
|
||||||
.ban(info.seen_addresses.iter().cloned().collect());
|
|
||||||
self.banned_peers_count
|
self.banned_peers_count
|
||||||
.add_banned_peer(&info.connection_status);
|
.add_banned_peer(info.seen_addresses());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
PeerConnectionStatus::Disconnecting { .. } => {
|
||||||
|
warn!(log_ref, "Banning peer that is currently disconnecting"; "peer_id" => %peer_id);
|
||||||
|
info.disconnecting(true);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
PeerConnectionStatus::Banned { .. } => {
|
||||||
|
error!(log_ref, "Banning already banned peer"; "peer_id" => %peer_id);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
PeerConnectionStatus::Connected { .. } | PeerConnectionStatus::Dialing { .. } => {
|
||||||
|
// update the state
|
||||||
|
info.disconnecting(true);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
PeerConnectionStatus::Unknown => {
|
||||||
|
// shift the peer straight to banned
|
||||||
|
warn!(log_ref, "Banning a peer of unknown connection state"; "peer_id" => %peer_id);
|
||||||
|
self.banned_peers_count
|
||||||
|
.add_banned_peer(info.seen_addresses());
|
||||||
|
info.ban();
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unbans a peer.
|
/// Unbans a peer.
|
||||||
pub fn unban(&mut self, peer_id: &PeerId) {
|
/// This should only be called once a peer's score is no longer banned.
|
||||||
|
/// If this is called for a banned peer, it will error.
|
||||||
|
pub fn unban(&mut self, peer_id: &PeerId) -> Result<(), &'static str> {
|
||||||
let log_ref = &self.log;
|
let log_ref = &self.log;
|
||||||
let info = self.peers.entry(peer_id.clone()).or_insert_with(|| {
|
let info = self.peers.entry(peer_id.clone()).or_insert_with(|| {
|
||||||
warn!(log_ref, "UnBanning unknown peer";
|
warn!(log_ref, "UnBanning unknown peer";
|
||||||
@ -458,12 +514,21 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
PeerInfo::default()
|
PeerInfo::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
if info.connection_status.is_banned() {
|
if !info.is_banned() {
|
||||||
self.banned_peers_count
|
return Err("Unbanning peer that is not banned");
|
||||||
.remove_banned_peer(&info.connection_status);
|
|
||||||
info.connection_status.unban();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ScoreState::Banned = info.score().state() {
|
||||||
|
return Err("Attempted to unban (connection status) a banned peer");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.banned_peers_count
|
||||||
|
.remove_banned_peer(info.seen_addresses());
|
||||||
|
info.unban();
|
||||||
|
// This transitions a banned peer to a disconnected peer
|
||||||
|
self.disconnected_peers = self.disconnected_peers.saturating_add(1);
|
||||||
self.shrink_to_fit();
|
self.shrink_to_fit();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes banned and disconnected peers from the DB if we have reached any of our limits.
|
/// Removes banned and disconnected peers from the DB if we have reached any of our limits.
|
||||||
@ -475,7 +540,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
if let Some(to_drop) = if let Some((id, info)) = self
|
if let Some(to_drop) = if let Some((id, info)) = self
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| info.connection_status.is_banned())
|
.filter(|(_, info)| info.is_banned())
|
||||||
.min_by(|(_, info_a), (_, info_b)| {
|
.min_by(|(_, info_a), (_, info_b)| {
|
||||||
info_a
|
info_a
|
||||||
.score()
|
.score()
|
||||||
@ -483,7 +548,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
}) {
|
}) {
|
||||||
self.banned_peers_count
|
self.banned_peers_count
|
||||||
.remove_banned_peer(&info.connection_status);
|
.remove_banned_peer(info.seen_addresses());
|
||||||
Some(id.clone())
|
Some(id.clone())
|
||||||
} else {
|
} else {
|
||||||
// If there is no minimum, this is a coding error.
|
// If there is no minimum, this is a coding error.
|
||||||
@ -505,7 +570,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
|
|||||||
if let Some(to_drop) = self
|
if let Some(to_drop) = self
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, info)| info.connection_status.is_disconnected())
|
.filter(|(_, info)| info.is_disconnected())
|
||||||
.min_by(|(_, info_a), (_, info_b)| {
|
.min_by(|(_, info_a), (_, info_b)| {
|
||||||
info_a
|
info_a
|
||||||
.score()
|
.score()
|
||||||
@ -570,6 +635,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_score<TSpec: EthSpec>(db: &mut PeerDB<TSpec>, peer_id: &PeerId) {
|
||||||
|
if let Some(info) = db.peer_info_mut(peer_id) {
|
||||||
|
info.reset_score();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_db() -> PeerDB<M> {
|
fn get_db() -> PeerDB<M> {
|
||||||
let log = build_log(slog::Level::Debug, false);
|
let log = build_log(slog::Level::Debug, false);
|
||||||
PeerDB::new(vec![], &log)
|
PeerDB::new(vec![], &log)
|
||||||
@ -597,11 +668,8 @@ mod tests {
|
|||||||
assert_eq!(pdb.score(&random_peer).score(), Score::default().score());
|
assert_eq!(pdb.score(&random_peer).score(), Score::default().score());
|
||||||
// it should be connected, and therefore not counted as disconnected
|
// it should be connected, and therefore not counted as disconnected
|
||||||
assert_eq!(pdb.disconnected_peers, 0);
|
assert_eq!(pdb.disconnected_peers, 0);
|
||||||
assert!(peer_info.unwrap().connection_status.is_connected());
|
assert!(peer_info.unwrap().is_connected());
|
||||||
assert_eq!(
|
assert_eq!(peer_info.unwrap().connections(), (n_in, n_out));
|
||||||
peer_info.unwrap().connection_status.connections(),
|
|
||||||
(n_in, n_out)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -615,7 +683,7 @@ mod tests {
|
|||||||
assert_eq!(pdb.disconnected_peers, 0);
|
assert_eq!(pdb.disconnected_peers, 0);
|
||||||
|
|
||||||
for p in pdb.connected_peer_ids().cloned().collect::<Vec<_>>() {
|
for p in pdb.connected_peer_ids().cloned().collect::<Vec<_>>() {
|
||||||
pdb.disconnect(&p);
|
pdb.notify_disconnect(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(pdb.disconnected_peers, MAX_DC_PEERS);
|
assert_eq!(pdb.disconnected_peers, MAX_DC_PEERS);
|
||||||
@ -632,7 +700,8 @@ mod tests {
|
|||||||
assert_eq!(pdb.banned_peers_count.banned_peers(), 0);
|
assert_eq!(pdb.banned_peers_count.banned_peers(), 0);
|
||||||
|
|
||||||
for p in pdb.connected_peer_ids().cloned().collect::<Vec<_>>() {
|
for p in pdb.connected_peer_ids().cloned().collect::<Vec<_>>() {
|
||||||
pdb.ban(&p);
|
pdb.disconnect_and_ban(&p);
|
||||||
|
pdb.notify_disconnect(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(pdb.banned_peers_count.banned_peers(), MAX_BANNED_PEERS);
|
assert_eq!(pdb.banned_peers_count.banned_peers(), MAX_BANNED_PEERS);
|
||||||
@ -653,7 +722,7 @@ mod tests {
|
|||||||
add_score(&mut pdb, &p2, 50.0);
|
add_score(&mut pdb, &p2, 50.0);
|
||||||
|
|
||||||
let best_peers: Vec<&PeerId> = pdb
|
let best_peers: Vec<&PeerId> = pdb
|
||||||
.best_peers_by_status(PeerConnectionStatus::is_connected)
|
.best_peers_by_status(PeerInfo::is_connected)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.0)
|
.map(|p| p.0)
|
||||||
.collect();
|
.collect();
|
||||||
@ -674,10 +743,10 @@ mod tests {
|
|||||||
add_score(&mut pdb, &p1, 100.0);
|
add_score(&mut pdb, &p1, 100.0);
|
||||||
add_score(&mut pdb, &p2, 50.0);
|
add_score(&mut pdb, &p2, 50.0);
|
||||||
|
|
||||||
let the_best = pdb.best_by_status(PeerConnectionStatus::is_connected);
|
let the_best = pdb.best_by_status(PeerInfo::is_connected);
|
||||||
assert!(the_best.is_some());
|
assert!(the_best.is_some());
|
||||||
// Consistency check
|
// Consistency check
|
||||||
let best_peers = pdb.best_peers_by_status(PeerConnectionStatus::is_connected);
|
let best_peers = pdb.best_peers_by_status(PeerInfo::is_connected);
|
||||||
assert_eq!(the_best, best_peers.iter().next().map(|p| p.0));
|
assert_eq!(the_best, best_peers.iter().next().map(|p| p.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,35 +758,27 @@ mod tests {
|
|||||||
|
|
||||||
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
|
||||||
|
|
||||||
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
pdb.notify_disconnect(&random_peer);
|
||||||
pdb.disconnect(&random_peer);
|
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
|
||||||
|
|
||||||
pdb.connect_outgoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_outgoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
pdb.notify_disconnect(&random_peer);
|
||||||
pdb.disconnect(&random_peer);
|
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
|
||||||
|
|
||||||
pdb.ban(&random_peer);
|
pdb.disconnect_and_ban(&random_peer);
|
||||||
|
pdb.notify_disconnect(&random_peer);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
pdb.notify_disconnect(&random_peer);
|
||||||
pdb.disconnect(&random_peer);
|
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
|
||||||
|
|
||||||
pdb.disconnect(&random_peer);
|
pdb.notify_disconnect(&random_peer);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
pdb.notify_disconnect(&random_peer);
|
||||||
pdb.disconnect(&random_peer);
|
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
dbg!("1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -740,15 +801,17 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
pdb.disconnect(&random_peer1);
|
pdb.notify_disconnect(&random_peer1);
|
||||||
pdb.ban(&random_peer2);
|
pdb.disconnect_and_ban(&random_peer2);
|
||||||
|
pdb.notify_disconnect(&random_peer2);
|
||||||
pdb.connect_ingoing(&random_peer3, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_ingoing(&random_peer3, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
pdb.banned_peers().count()
|
pdb.banned_peers().count()
|
||||||
);
|
);
|
||||||
pdb.ban(&random_peer1);
|
pdb.disconnect_and_ban(&random_peer1);
|
||||||
|
pdb.notify_disconnect(&random_peer1);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
@ -761,37 +824,41 @@ mod tests {
|
|||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
pdb.banned_peers().count()
|
pdb.banned_peers().count()
|
||||||
);
|
);
|
||||||
pdb.ban(&random_peer3);
|
pdb.disconnect_and_ban(&random_peer3);
|
||||||
|
pdb.notify_disconnect(&random_peer3);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
pdb.banned_peers().count()
|
pdb.banned_peers().count()
|
||||||
);
|
);
|
||||||
|
|
||||||
pdb.ban(&random_peer3);
|
pdb.disconnect_and_ban(&random_peer3);
|
||||||
|
pdb.notify_disconnect(&random_peer3);
|
||||||
pdb.connect_ingoing(&random_peer1, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_ingoing(&random_peer1, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
pdb.disconnect(&random_peer2);
|
pdb.notify_disconnect(&random_peer2);
|
||||||
pdb.ban(&random_peer3);
|
pdb.disconnect_and_ban(&random_peer3);
|
||||||
|
pdb.notify_disconnect(&random_peer3);
|
||||||
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
pdb.banned_peers().count()
|
pdb.banned_peers().count()
|
||||||
);
|
);
|
||||||
pdb.disconnect(&random_peer);
|
pdb.notify_disconnect(&random_peer);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
pdb.banned_peers().count()
|
pdb.banned_peers().count()
|
||||||
);
|
);
|
||||||
|
|
||||||
pdb.disconnect(&random_peer);
|
pdb.notify_disconnect(&random_peer);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pdb.banned_peers_count.banned_peers(),
|
pdb.banned_peers_count.banned_peers(),
|
||||||
pdb.banned_peers().count()
|
pdb.banned_peers().count()
|
||||||
);
|
);
|
||||||
pdb.ban(&random_peer);
|
pdb.disconnect_and_ban(&random_peer);
|
||||||
|
pdb.notify_disconnect(&random_peer);
|
||||||
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -835,7 +902,8 @@ mod tests {
|
|||||||
let p5 = connect_peer_with_ips(&mut pdb, vec![ip5]);
|
let p5 = connect_peer_with_ips(&mut pdb, vec![ip5]);
|
||||||
|
|
||||||
for p in &peers[..BANNED_PEERS_PER_IP_THRESHOLD + 1] {
|
for p in &peers[..BANNED_PEERS_PER_IP_THRESHOLD + 1] {
|
||||||
pdb.ban(p);
|
pdb.disconnect_and_ban(p);
|
||||||
|
pdb.notify_disconnect(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
//check that ip1 and ip2 are banned but ip3-5 not
|
//check that ip1 and ip2 are banned but ip3-5 not
|
||||||
@ -846,7 +914,8 @@ mod tests {
|
|||||||
assert!(!pdb.is_banned(&p5));
|
assert!(!pdb.is_banned(&p5));
|
||||||
|
|
||||||
//ban also the last peer in peers
|
//ban also the last peer in peers
|
||||||
pdb.ban(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]);
|
pdb.disconnect_and_ban(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]);
|
||||||
|
pdb.notify_disconnect(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]);
|
||||||
|
|
||||||
//check that ip1-ip4 are banned but ip5 not
|
//check that ip1-ip4 are banned but ip5 not
|
||||||
assert!(pdb.is_banned(&p1));
|
assert!(pdb.is_banned(&p1));
|
||||||
@ -856,7 +925,8 @@ mod tests {
|
|||||||
assert!(!pdb.is_banned(&p5));
|
assert!(!pdb.is_banned(&p5));
|
||||||
|
|
||||||
//peers[0] gets unbanned
|
//peers[0] gets unbanned
|
||||||
pdb.unban(&peers[0]);
|
reset_score(&mut pdb, &peers[0]);
|
||||||
|
pdb.unban(&peers[0]).unwrap();
|
||||||
|
|
||||||
//nothing changed
|
//nothing changed
|
||||||
assert!(pdb.is_banned(&p1));
|
assert!(pdb.is_banned(&p1));
|
||||||
@ -866,7 +936,8 @@ mod tests {
|
|||||||
assert!(!pdb.is_banned(&p5));
|
assert!(!pdb.is_banned(&p5));
|
||||||
|
|
||||||
//peers[1] gets unbanned
|
//peers[1] gets unbanned
|
||||||
pdb.unban(&peers[1]);
|
reset_score(&mut pdb, &peers[1]);
|
||||||
|
pdb.unban(&peers[1]).unwrap();
|
||||||
|
|
||||||
//all ips are unbanned
|
//all ips are unbanned
|
||||||
assert!(!pdb.is_banned(&p1));
|
assert!(!pdb.is_banned(&p1));
|
||||||
@ -893,52 +964,43 @@ mod tests {
|
|||||||
|
|
||||||
// ban all peers
|
// ban all peers
|
||||||
for p in &peers {
|
for p in &peers {
|
||||||
pdb.ban(p);
|
pdb.disconnect_and_ban(p);
|
||||||
|
pdb.notify_disconnect(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check ip is banned
|
// check ip is banned
|
||||||
assert!(pdb.is_banned(&p1));
|
assert!(pdb.is_banned(&p1));
|
||||||
assert!(!pdb.is_banned(&p2));
|
assert!(!pdb.is_banned(&p2));
|
||||||
|
|
||||||
// change addresses of banned peers
|
|
||||||
for p in &peers {
|
|
||||||
let seen_addresses = &mut pdb.peers.get_mut(p).unwrap().seen_addresses;
|
|
||||||
seen_addresses.clear();
|
|
||||||
seen_addresses.insert(ip2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check still the same ip is banned
|
|
||||||
assert!(pdb.is_banned(&p1));
|
|
||||||
assert!(!pdb.is_banned(&p2));
|
|
||||||
|
|
||||||
// unban a peer
|
// unban a peer
|
||||||
pdb.unban(&peers[0]);
|
reset_score(&mut pdb, &peers[0]);
|
||||||
|
pdb.unban(&peers[0]).unwrap();
|
||||||
|
|
||||||
// check not banned anymore
|
// check not banned anymore
|
||||||
assert!(!pdb.is_banned(&p1));
|
assert!(!pdb.is_banned(&p1));
|
||||||
assert!(!pdb.is_banned(&p2));
|
assert!(!pdb.is_banned(&p2));
|
||||||
|
|
||||||
// unban and reban all peers
|
// add ip2 to all peers and ban them.
|
||||||
for p in &peers {
|
for p in &peers {
|
||||||
pdb.unban(p);
|
pdb.connect_ingoing(&p, ip2.into(), None);
|
||||||
pdb.ban(p);
|
pdb.disconnect_and_ban(p);
|
||||||
|
pdb.notify_disconnect(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ip2 is now banned
|
// both IP's are now banned
|
||||||
assert!(!pdb.is_banned(&p1));
|
assert!(pdb.is_banned(&p1));
|
||||||
assert!(pdb.is_banned(&p2));
|
assert!(pdb.is_banned(&p2));
|
||||||
|
|
||||||
// change ips back again
|
// unban all peers
|
||||||
for p in &peers {
|
for p in &peers {
|
||||||
let seen_addresses = &mut pdb.peers.get_mut(p).unwrap().seen_addresses;
|
reset_score(&mut pdb, &p);
|
||||||
seen_addresses.clear();
|
pdb.unban(p).unwrap();
|
||||||
seen_addresses.insert(ip1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reban every peer except one
|
// reban every peer except one
|
||||||
for p in &peers[1..] {
|
for p in &peers[1..] {
|
||||||
pdb.unban(p);
|
pdb.disconnect_and_ban(p);
|
||||||
pdb.ban(p);
|
pdb.notify_disconnect(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// nothing is banned
|
// nothing is banned
|
||||||
@ -946,12 +1008,12 @@ mod tests {
|
|||||||
assert!(!pdb.is_banned(&p2));
|
assert!(!pdb.is_banned(&p2));
|
||||||
|
|
||||||
//reban last peer
|
//reban last peer
|
||||||
pdb.unban(&peers[0]);
|
pdb.disconnect_and_ban(&peers[0]);
|
||||||
pdb.ban(&peers[0]);
|
pdb.notify_disconnect(&peers[0]);
|
||||||
|
|
||||||
//ip1 is banned
|
//Ip's are banned again
|
||||||
assert!(pdb.is_banned(&p1));
|
assert!(pdb.is_banned(&p1));
|
||||||
assert!(!pdb.is_banned(&p2));
|
assert!(pdb.is_banned(&p2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -48,7 +48,7 @@ pub enum PeerAction {
|
|||||||
/// An error occurred with this peer but it is not necessarily malicious.
|
/// An error occurred with this peer but it is not necessarily malicious.
|
||||||
/// We have high tolerance for this actions: several occurrences are needed for a peer to get
|
/// We have high tolerance for this actions: several occurrences are needed for a peer to get
|
||||||
/// kicked.
|
/// kicked.
|
||||||
/// NOTE: ~15 occurrences will get the peer banned
|
/// NOTE: ~50 occurrences will get the peer banned
|
||||||
HighToleranceError,
|
HighToleranceError,
|
||||||
/// Received an expected message.
|
/// Received an expected message.
|
||||||
_ValidMessage,
|
_ValidMessage,
|
||||||
@ -178,7 +178,7 @@ impl Score {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add an f64 to the score abiding by the limits.
|
/// Add an f64 to the score abiding by the limits.
|
||||||
pub fn add(&mut self, score: f64) {
|
fn add(&mut self, score: f64) {
|
||||||
let mut new_score = self.score + score;
|
let mut new_score = self.score + score;
|
||||||
if new_score > MAX_SCORE {
|
if new_score > MAX_SCORE {
|
||||||
new_score = MAX_SCORE;
|
new_score = MAX_SCORE;
|
||||||
@ -190,6 +190,26 @@ impl Score {
|
|||||||
self.score = new_score;
|
self.score = new_score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add an f64 to the score abiding by the limits.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn test_add(&mut self, score: f64) {
|
||||||
|
let mut new_score = self.score + score;
|
||||||
|
if new_score > MAX_SCORE {
|
||||||
|
new_score = MAX_SCORE;
|
||||||
|
}
|
||||||
|
if new_score < MIN_SCORE {
|
||||||
|
new_score = MIN_SCORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.score = new_score;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
// reset the score
|
||||||
|
pub fn test_reset(&mut self) {
|
||||||
|
self.score = 0f64;
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies time-based logic such as decay rates to the score.
|
/// Applies time-based logic such as decay rates to the score.
|
||||||
/// This function should be called periodically.
|
/// This function should be called periodically.
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {
|
||||||
|
@ -1259,7 +1259,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
&dir,
|
&dir,
|
||||||
),
|
),
|
||||||
state: api_types::PeerState::from_peer_connection_status(
|
state: api_types::PeerState::from_peer_connection_status(
|
||||||
&peer_info.connection_status,
|
&peer_info.connection_status(),
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -1301,7 +1301,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
&dir,
|
&dir,
|
||||||
),
|
),
|
||||||
state: api_types::PeerState::from_peer_connection_status(
|
state: api_types::PeerState::from_peer_connection_status(
|
||||||
&peer_info.connection_status,
|
&peer_info.connection_status(),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -444,6 +444,7 @@ impl PeerState {
|
|||||||
match status {
|
match status {
|
||||||
PeerConnectionStatus::Connected { .. } => PeerState::Connected,
|
PeerConnectionStatus::Connected { .. } => PeerState::Connected,
|
||||||
PeerConnectionStatus::Dialing { .. } => PeerState::Connecting,
|
PeerConnectionStatus::Dialing { .. } => PeerState::Connecting,
|
||||||
|
PeerConnectionStatus::Disconnecting { .. } => PeerState::Disconnecting,
|
||||||
PeerConnectionStatus::Disconnected { .. }
|
PeerConnectionStatus::Disconnected { .. }
|
||||||
| PeerConnectionStatus::Banned { .. }
|
| PeerConnectionStatus::Banned { .. }
|
||||||
| PeerConnectionStatus::Unknown => PeerState::Disconnected,
|
| PeerConnectionStatus::Unknown => PeerState::Disconnected,
|
||||||
|
Loading…
Reference in New Issue
Block a user