use crate::{
peer_gossip::Shared, peer_limits::Class, peer_reach::Remotes, peer_transport::Behaviour,
};
use libp2p::{PeerId, Swarm, futures::StreamExt};
use std::time::Duration;
const UNPROVEN: Duration = Duration::from_secs(60);
const DEMOTED: Duration = Duration::from_secs(60);
const DRAIN: Duration = Duration::from_millis(500);
const FLUSH: Duration = Duration::from_millis(100);
pub(crate) fn release(
(swarm, remotes): (&mut Swarm<Behaviour>, &mut Remotes),
shared: &Shared,
peer: PeerId,
) {
for (id, outbound) in remotes.data_of(peer) {
if swarm.behaviour_mut().limits.demote(id) {
remotes.reclass(id, Class::Demoted);
shared.reclassed(peer, outbound, (Class::Data, Class::Demoted));
tracing::debug!(%peer, "released peer demoted");
} else {
tracing::debug!(%peer, "holder budget spent; released link closed");
swarm.close_connection(id);
}
}
}
pub(crate) fn maintain((swarm, remotes): (&mut Swarm<Behaviour>, &mut Remotes), shared: &Shared) {
for peer in remotes.unproven(UNPROVEN, |peer| shared.is_on_chain(peer)) {
tracing::debug!(%peer, "data connection never proved its chain; released");
release((swarm, remotes), shared, peer);
}
for (id, peer, outbound) in remotes.demoted_after(DEMOTED) {
if shared.is_on_chain(peer) && swarm.behaviour_mut().limits.promote(id) {
remotes.reclass(id, Class::Data);
shared.reclassed(peer, outbound, (Class::Demoted, Class::Data));
}
}
}
pub(crate) async fn close_all(swarm: &mut Swarm<Behaviour>) {
let peers: Vec<PeerId> = swarm.connected_peers().copied().collect();
for peer in peers {
if swarm.disconnect_peer_id(peer).is_err() {
tracing::debug!(%peer, "peer left before shutdown closed it");
}
}
let closed = tokio::time::timeout(DRAIN, async {
while swarm.network_info().num_peers() > 0 {
swarm.select_next_some().await;
}
})
.await;
if closed.is_err() {
tracing::debug!("shutdown left connections closing");
}
tokio::time::sleep(FLUSH).await;
}