meta-transport-ipfs 1.0.0: ipfs tier split out of meta-share
Publish image / publish (push) Failing after 14s
Publish image / publish (push) Failing after 14s
This commit is contained in:
@@ -0,0 +1,558 @@
|
||||
//! The swarm task — owns the libp2p `Swarm`, the HTTP command channel,
|
||||
//! and the kad-DHT/bootstrap/peer-directory state, and drives them all
|
||||
//! from one `tokio::select!` loop.
|
||||
//!
|
||||
//! Refactored from the previous monolithic `swarm::spawn` closure into a
|
||||
//! `SwarmTask` struct with per-arm handler methods. The select! itself
|
||||
//! stays in [`SwarmTask::run`] because tokio's macro needs all arm futures
|
||||
//! to be constructed in one scope — `swarm` and `rx` are passed in as
|
||||
//! locals (not held on `self`) so each handler method can borrow
|
||||
//! `&mut self` cleanly without conflicting with the arm futures.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::StreamExt;
|
||||
use libp2p::core::ConnectedPoint;
|
||||
use libp2p::kad::{self, GetProvidersOk, QueryResult, RecordKey};
|
||||
use libp2p::mdns;
|
||||
use libp2p::swarm::SwarmEvent;
|
||||
use libp2p::{identify, Multiaddr, PeerId, Swarm};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, info, trace, warn};
|
||||
|
||||
use crate::gateway_discovery::{advertises_gateways, fetch_gateway_caps};
|
||||
|
||||
use super::bootstrap::{addr_targets_match, redial_bootstrap, BootstrapEntry};
|
||||
use super::bitswap_client::BitswapInflight;
|
||||
use super::identify_agent::parse_base_url;
|
||||
use super::kad_discovery::{namespace_key, peer_id_from_multiaddr, KadConfig};
|
||||
use super::peer_directory::PeerDirectory;
|
||||
use super::{Behaviour, BehaviourEvent, Command, GatewayCapsConfig, PeersInfo};
|
||||
|
||||
/// All the state that lives across iterations of the event loop. `swarm`
|
||||
/// and the channel receivers stay as locals in [`SwarmTask::run`] so the
|
||||
/// `tokio::select!` arms can borrow them without conflicting with
|
||||
/// `&mut self` calls in the arm bodies.
|
||||
pub(super) struct SwarmTask {
|
||||
local_peer_id: PeerId,
|
||||
/// Bootstrap targets we keep alive across reconnects.
|
||||
bootstrap: Vec<BootstrapEntry>,
|
||||
/// Shared peer→info directory (populated from identify events).
|
||||
peer_directory: PeerDirectory,
|
||||
/// kad-DHT discovery state.
|
||||
kad_namespace: String,
|
||||
ns_key: RecordKey,
|
||||
kad_target_peer_count: usize,
|
||||
kad_fast_discovery_interval: Duration,
|
||||
kad_discovery_interval: Duration,
|
||||
kad_provide_interval: Duration,
|
||||
/// Whether to publish the namespace provider record at all — see
|
||||
/// [`KadConfig::provide_enabled`]. False when this peer isn't publicly
|
||||
/// reachable.
|
||||
kad_provide_enabled: bool,
|
||||
kad_bootstrap_peers: Vec<Multiaddr>,
|
||||
/// Rate-limit floor for reactive kad kicks on `ConnectionClosed`.
|
||||
last_kad_query_at: tokio::time::Instant,
|
||||
/// Auto-redial cadence; zero / empty bootstrap effectively disables.
|
||||
redial_interval: Duration,
|
||||
/// In-flight `bitswap.get(cid)` calls awaiting block delivery.
|
||||
/// Populated by `Command::BitswapGet`, drained by the
|
||||
/// `BehaviourEvent::Bitswap` arm of `on_swarm_event`.
|
||||
bitswap_inflight: BitswapInflight,
|
||||
/// Process-wide HTTP client, for the gateway capability fetch off identify.
|
||||
http: reqwest::Client,
|
||||
/// Timeout + debounce for that fetch.
|
||||
gateway_caps: GatewayCapsConfig,
|
||||
}
|
||||
|
||||
impl SwarmTask {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn new(
|
||||
local_peer_id: PeerId,
|
||||
bootstrap_peers: Vec<Multiaddr>,
|
||||
redial_interval: Duration,
|
||||
kad_config: KadConfig,
|
||||
peer_directory: PeerDirectory,
|
||||
http: reqwest::Client,
|
||||
gateway_caps: GatewayCapsConfig,
|
||||
) -> Self {
|
||||
let bootstrap: Vec<BootstrapEntry> =
|
||||
bootstrap_peers.into_iter().map(BootstrapEntry::new).collect();
|
||||
let ns_key = namespace_key(&kad_config.namespace);
|
||||
// Initialise far enough in the past that the very first reactive kick
|
||||
// can fire (subject to the fast-discovery rate-limit floor).
|
||||
let last_kad_query_at = tokio::time::Instant::now()
|
||||
.checked_sub(kad_config.fast_discovery_interval)
|
||||
.unwrap_or_else(tokio::time::Instant::now);
|
||||
Self {
|
||||
local_peer_id,
|
||||
bootstrap,
|
||||
peer_directory,
|
||||
kad_namespace: kad_config.namespace,
|
||||
ns_key,
|
||||
kad_target_peer_count: kad_config.target_peer_count,
|
||||
kad_fast_discovery_interval: kad_config.fast_discovery_interval,
|
||||
kad_discovery_interval: kad_config.discovery_interval,
|
||||
kad_provide_interval: kad_config.provide_interval,
|
||||
kad_provide_enabled: kad_config.provide_enabled,
|
||||
kad_bootstrap_peers: kad_config.bootstrap_peers,
|
||||
last_kad_query_at,
|
||||
redial_interval,
|
||||
bitswap_inflight: BitswapInflight::new(),
|
||||
http,
|
||||
gateway_caps,
|
||||
}
|
||||
}
|
||||
|
||||
/// Drive the swarm task to completion. Owns `swarm` and the channel
|
||||
/// receivers as locals — the `tokio::select!` arms borrow these
|
||||
/// directly, and the arm bodies are free to call `&mut self` methods
|
||||
/// because the arm futures have completed (and their borrows
|
||||
/// released) by the time a body runs.
|
||||
pub(super) async fn run(
|
||||
mut self,
|
||||
mut swarm: Swarm<Behaviour>,
|
||||
mut rx: mpsc::Receiver<Command>,
|
||||
) {
|
||||
// Initial bootstrap dials.
|
||||
for entry in &self.bootstrap {
|
||||
match swarm.dial(entry.addr.clone()) {
|
||||
Ok(()) => info!(addr = %entry.addr, "bootstrap dial issued"),
|
||||
Err(e) => warn!(addr = %entry.addr, error = %e, "bootstrap dial failed"),
|
||||
}
|
||||
}
|
||||
|
||||
// Seed kad's routing table with the configured bootstrap multiaddrs.
|
||||
// Each entry must include `/p2p/<peer_id>` so we can register the
|
||||
// peer-id without an extra round-trip; entries without are skipped
|
||||
// with a warning (the kad routing table is keyed by peer-id).
|
||||
let mut kad_seeded = 0usize;
|
||||
for addr in &self.kad_bootstrap_peers {
|
||||
match peer_id_from_multiaddr(addr) {
|
||||
Some(pid) => {
|
||||
swarm.behaviour_mut().kad.add_address(&pid, addr.clone());
|
||||
kad_seeded += 1;
|
||||
debug!(%pid, %addr, "kad: bootstrap address registered");
|
||||
}
|
||||
None => warn!(%addr, "kad: bootstrap multiaddr lacks /p2p/<id>; skipped"),
|
||||
}
|
||||
}
|
||||
if kad_seeded > 0 {
|
||||
match swarm.behaviour_mut().kad.bootstrap() {
|
||||
Ok(qid) => info!(?qid, kad_seeded, "kad: bootstrap query issued"),
|
||||
Err(e) => warn!(error = %e, "kad: bootstrap query failed"),
|
||||
}
|
||||
} else {
|
||||
// Not a fault — it's the local-mode default. An empty bootstrap list
|
||||
// is exactly what keeps an unreachable peer off the public DHT, so
|
||||
// saying it at WARN would train operators to ignore a real warning.
|
||||
info!(
|
||||
"kad: no bootstrap peers configured; public-DHT discovery is off \
|
||||
(local mode — mDNS still works). Set PUBLIC_ADDR to join the public DHT."
|
||||
);
|
||||
}
|
||||
info!(namespace = %self.kad_namespace, "kad: discovery namespace");
|
||||
|
||||
// Auto-redial ticker. `redial_interval == 0` disables; we still need a
|
||||
// ticker (`tokio::select!` requires all branches to compile) so use a
|
||||
// very long interval that effectively never fires.
|
||||
let effective_interval = if self.redial_interval.is_zero() || self.bootstrap.is_empty() {
|
||||
Duration::from_secs(60 * 60 * 24 * 365) // 1y; effectively never
|
||||
} else {
|
||||
self.redial_interval
|
||||
};
|
||||
let mut redial_tick = tokio::time::interval(effective_interval);
|
||||
// Skip the immediate first tick — we just did the initial dials above.
|
||||
redial_tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
||||
redial_tick.tick().await;
|
||||
|
||||
// kad provide ticker — fires immediately so we publish on startup,
|
||||
// then every kad_provide_interval. Provider records on remote peers
|
||||
// expire (~24 h default), so this must be lower than that.
|
||||
let mut kad_provide_tick = tokio::time::interval(self.kad_provide_interval);
|
||||
kad_provide_tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
||||
|
||||
// kad discovery ticker — same pattern, fires immediately.
|
||||
let mut kad_discovery_tick = tokio::time::interval(self.kad_discovery_interval);
|
||||
kad_discovery_tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
cmd = rx.recv() => match cmd {
|
||||
Some(cmd) => self.on_command(&mut swarm, cmd).await,
|
||||
None => {
|
||||
debug!("command channel closed; shutting down swarm task");
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = redial_tick.tick() => {
|
||||
redial_bootstrap(&mut swarm, &self.bootstrap);
|
||||
},
|
||||
_ = kad_provide_tick.tick() => {
|
||||
// A provider record is a promise that this peer can be
|
||||
// dialed. In local mode it can't be (no public address,
|
||||
// only container-internal listeners), so publishing one
|
||||
// would send every peer that walks the DHT into a dial
|
||||
// that can never connect. Stay quiet.
|
||||
if self.kad_provide_enabled {
|
||||
match swarm.behaviour_mut().kad.start_providing(self.ns_key.clone()) {
|
||||
Ok(qid) => debug!(?qid, namespace = %self.kad_namespace,
|
||||
"kad: re-publishing provider record"),
|
||||
Err(e) => warn!(error = %e, namespace = %self.kad_namespace,
|
||||
"kad: start_providing failed"),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ = kad_discovery_tick.tick() => {
|
||||
self.on_kad_discovery_tick(&mut swarm, &mut kad_discovery_tick);
|
||||
},
|
||||
event = swarm.select_next_some() => {
|
||||
self.on_swarm_event(&mut swarm, event, &mut kad_discovery_tick).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a single HTTP-layer command.
|
||||
async fn on_command(&mut self, swarm: &mut Swarm<Behaviour>, cmd: Command) {
|
||||
match cmd {
|
||||
Command::Peers { reply } => {
|
||||
let connected: Vec<String> =
|
||||
swarm.connected_peers().map(|p| p.to_string()).collect();
|
||||
let info = PeersInfo {
|
||||
local_peer_id: self.local_peer_id.to_string(),
|
||||
connected,
|
||||
};
|
||||
let _ = reply.send(info);
|
||||
}
|
||||
Command::BitswapGet { cid, reply } => {
|
||||
// M12: issue a bitswap fetch for one block. The
|
||||
// behaviour fans WANT-HAVE / WANT-BLOCK to connected
|
||||
// peers (gateways + sibling consumers that already
|
||||
// cached the cid). `bitswap.get` returns a QueryId;
|
||||
// we register it with the inflight map and the
|
||||
// `BehaviourEvent::Bitswap` arm fires the reply when
|
||||
// either `GetQueryResponse` (success) or
|
||||
// `GetQueryError` (transport / no-providers /
|
||||
// timeout) arrives.
|
||||
let query_id = swarm.behaviour_mut().bitswap.get(&cid);
|
||||
tracing::debug!(%cid, ?query_id, "bitswap: issued .get()");
|
||||
self.bitswap_inflight.register(query_id, reply);
|
||||
}
|
||||
Command::Provide { cid } => {
|
||||
// Announce on the public IPFS DHT, keyed by the CID's
|
||||
// multihash bytes — that's the kubo-compatible provider key,
|
||||
// so a vanilla IPFS node `get_providers`ing the same content
|
||||
// finds us. The StartProviding result is logged in
|
||||
// `default_log`. Works in `Mode::Client` (same call the
|
||||
// gateway makes).
|
||||
let key = RecordKey::new(&cid.hash().to_bytes());
|
||||
match swarm.behaviour_mut().kad.start_providing(key) {
|
||||
Ok(qid) => debug!(?qid, %cid, "kad: providing seeded cid"),
|
||||
Err(e) => warn!(error = %e, %cid, "kad: start_providing(cid) failed"),
|
||||
}
|
||||
}
|
||||
Command::StopProviding { cid } => {
|
||||
let key = RecordKey::new(&cid.hash().to_bytes());
|
||||
swarm.behaviour_mut().kad.stop_providing(&key);
|
||||
debug!(%cid, "kad: stopped providing seeded cid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fired every `kad_discovery_interval` (steady-state) or
|
||||
/// `kad_fast_discovery_interval` (below floor). Issues a fresh
|
||||
/// `get_providers` query and adapts the next tick's cadence based on
|
||||
/// the current connection count.
|
||||
fn on_kad_discovery_tick(
|
||||
&mut self,
|
||||
swarm: &mut Swarm<Behaviour>,
|
||||
kad_discovery_tick: &mut tokio::time::Interval,
|
||||
) {
|
||||
let connected = swarm.connected_peers().count();
|
||||
let qid = swarm.behaviour_mut().kad.get_providers(self.ns_key.clone());
|
||||
self.last_kad_query_at = tokio::time::Instant::now();
|
||||
// Adapt the next tick's cadence to whether we're below the
|
||||
// connection-floor. `reset_after` only affects the *next* tick —
|
||||
// current behaviour keeps the steady-state config-driven topup rate
|
||||
// when the swarm is full and accelerates discovery when it's not.
|
||||
let next = if connected < self.kad_target_peer_count {
|
||||
self.kad_fast_discovery_interval
|
||||
} else {
|
||||
self.kad_discovery_interval
|
||||
};
|
||||
debug!(
|
||||
?qid, namespace = %self.kad_namespace,
|
||||
connected, target = self.kad_target_peer_count,
|
||||
next_tick_secs = next.as_secs(),
|
||||
"kad: querying for providers"
|
||||
);
|
||||
kad_discovery_tick.reset_after(next);
|
||||
}
|
||||
|
||||
/// Top-level swarm-event dispatch. Each branch inspects `&event` and
|
||||
/// falls through to `default_log` at the end.
|
||||
async fn on_swarm_event(
|
||||
&mut self,
|
||||
swarm: &mut Swarm<Behaviour>,
|
||||
event: SwarmEvent<BehaviourEvent>,
|
||||
kad_discovery_tick: &mut tokio::time::Interval,
|
||||
) {
|
||||
// 0) Soft ceiling for inbound: when we're already at target,
|
||||
// close inbound connections before they negotiate. Pre-handshake
|
||||
// close — peer-id is not known yet, only the source address.
|
||||
// Bootstrap dials and our own outgoing dials are unaffected
|
||||
// (they go through the dial-gates in the mdns/kad branches
|
||||
// below).
|
||||
if let SwarmEvent::IncomingConnection { connection_id, .. } = &event {
|
||||
let cid = *connection_id;
|
||||
let connected = swarm.connected_peers().count();
|
||||
if connected >= self.kad_target_peer_count {
|
||||
debug!(?cid, connected, target = self.kad_target_peer_count,
|
||||
"rejecting inbound connection; at target");
|
||||
let _ = swarm.close_connection(cid);
|
||||
}
|
||||
}
|
||||
// 1) On every outgoing-dial connection, opportunistically populate
|
||||
// bootstrap[].peer_id so future redial ticks can skip
|
||||
// already-connected entries.
|
||||
if let SwarmEvent::ConnectionEstablished { peer_id, endpoint, .. } = &event {
|
||||
if let ConnectedPoint::Dialer { address, .. } = endpoint {
|
||||
// Always overwrite the cached peer-id on a fresh outgoing
|
||||
// dial, not just on first contact. Bootstrap targets that
|
||||
// lack a persistent peer-id (today: every meta-share peer
|
||||
// in the POC) rotate their identity on restart, and a
|
||||
// stale cached peer-id makes is_connected() return false
|
||||
// on the next tick — causing a redundant dial that
|
||||
// doubles the connection count.
|
||||
for entry in self.bootstrap.iter_mut() {
|
||||
if addr_targets_match(&entry.addr, address)
|
||||
&& entry.peer_id != Some(*peer_id)
|
||||
{
|
||||
entry.peer_id = Some(*peer_id);
|
||||
debug!(peer_id = %peer_id, addr = %entry.addr,
|
||||
"auto-redial: bootstrap target peer-id learned/refreshed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 1b) On disconnect: if we drop below the connection floor, kick
|
||||
// the kad discovery tick to fire immediately so a replacement
|
||||
// peer is found quickly — but only if the last query was at
|
||||
// least `fast_discovery_interval` ago, so a flapping peer
|
||||
// can't trigger DHT queries faster than that floor.
|
||||
// `reset_after(0)` makes the *next* tick fire now; if we're
|
||||
// already mid-query the in-flight one continues.
|
||||
if let SwarmEvent::ConnectionClosed { peer_id, num_established, .. } = &event {
|
||||
// On the *last* connection to a peer closing, drop its gateway
|
||||
// state immediately — the fast complement to the read-side TTL
|
||||
// in PeerDirectory. A peer whose libp2p id rotates on restart
|
||||
// (the dev-stack norm) otherwise keeps advertising upstreams it
|
||||
// no longer serves, and the M7 fan-out burns a slot dialing the
|
||||
// dead id. `num_established` is the count remaining *after* this
|
||||
// close, so 0 means the peer is fully gone.
|
||||
if *num_established == 0 && self.peer_directory.forget_peer(peer_id) {
|
||||
debug!(peer_id = %peer_id, "peer fully disconnected — forgot gateway state");
|
||||
}
|
||||
let connected = swarm.connected_peers().count();
|
||||
if connected < self.kad_target_peer_count
|
||||
&& self.last_kad_query_at.elapsed() >= self.kad_fast_discovery_interval
|
||||
{
|
||||
debug!(connected, target = self.kad_target_peer_count,
|
||||
"kad: below connection floor — kicking discovery");
|
||||
kad_discovery_tick.reset_after(Duration::ZERO);
|
||||
}
|
||||
}
|
||||
// 2) When the identify protocol delivers a peer's listen
|
||||
// addresses, feed them into kad's routing table and parse
|
||||
// `baseUrl=` into the peer directory (the byte-fetch path
|
||||
// resolves a peer's HTTP API URL from here).
|
||||
if let SwarmEvent::Behaviour(BehaviourEvent::Identify(
|
||||
identify::Event::Received { peer_id, info, .. }
|
||||
)) = &event {
|
||||
let pid = *peer_id;
|
||||
let addrs: Vec<Multiaddr> = info.listen_addrs.clone();
|
||||
for addr in addrs {
|
||||
swarm.behaviour_mut().kad.add_address(&pid, addr);
|
||||
}
|
||||
let base_url = parse_base_url(&info.agent_version).map(str::to_string);
|
||||
let update = self.peer_directory.upsert_from_identify(pid, base_url);
|
||||
if let Some(url) = &update.base_url_changed {
|
||||
debug!(peer_id = %pid, base_url = %url,
|
||||
"identify: learned/refreshed peer base URL");
|
||||
}
|
||||
// Gateway discovery. A non-empty `gateways=` token is emitted by
|
||||
// meta-gateway and nobody else, so it's the "this peer is a
|
||||
// gateway" signal. Its *capabilities* aren't on the wire though —
|
||||
// libp2p 0.56 can't mutate agent_version after startup, so a
|
||||
// runtime plugin toggle would never reach us. We fetch them over
|
||||
// plain HTTP off the just-learned baseUrl instead (the reliable
|
||||
// source that replaced the flaky gossipsub heartbeat).
|
||||
//
|
||||
// Fire-and-forget: a slow/dead gateway must not stall the swarm
|
||||
// loop. Debounced by the directory so the identify burst on a
|
||||
// multi-connection peer doesn't fan out into redundant fetches;
|
||||
// identify's ~5min re-announce then keeps the capability fresh
|
||||
// inside its 600s TTL, which is why there's no keep-alive tick.
|
||||
if advertises_gateways(&info.agent_version)
|
||||
&& self
|
||||
.peer_directory
|
||||
.needs_cap_refresh(&pid, self.gateway_caps.refresh_after)
|
||||
{
|
||||
if let Some(base_url) = self.peer_directory.base_url(&pid) {
|
||||
let http = self.http.clone();
|
||||
let dir = self.peer_directory.clone();
|
||||
let timeout = self.gateway_caps.fetch_timeout;
|
||||
tokio::spawn(async move {
|
||||
match fetch_gateway_caps(&http, &base_url, timeout).await {
|
||||
Ok(caps) => {
|
||||
let redeems = caps.redeem_claims();
|
||||
debug!(peer_id = %pid, base_url = %base_url,
|
||||
nzb_fetch = caps.nzb_fetch,
|
||||
redeem_claims = redeems.len(),
|
||||
"gateway caps: fetched");
|
||||
dir.record_gateway_caps(pid, caps.nzb_fetch, redeems);
|
||||
}
|
||||
// Best-effort: keep whatever we held (it ages out on
|
||||
// its own TTL) and retry on the next identify.
|
||||
Err(e) => debug!(peer_id = %pid, base_url = %base_url,
|
||||
error = %e, "gateway caps: fetch failed"),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2b) mDNS: register the address in kad's routing table (so
|
||||
// subsequent lookups dedupe) and dial — unless at target.
|
||||
if let SwarmEvent::Behaviour(BehaviourEvent::Mdns(
|
||||
mdns::Event::Discovered(peers)
|
||||
)) = &event {
|
||||
for (pid, addr) in peers.iter() {
|
||||
if *pid == self.local_peer_id {
|
||||
continue;
|
||||
}
|
||||
swarm.behaviour_mut().kad.add_address(pid, addr.clone());
|
||||
if swarm.is_connected(pid) {
|
||||
trace!(peer_id = %pid, %addr, "mdns: peer already connected");
|
||||
continue;
|
||||
}
|
||||
// LAN-local (mDNS) peers bypass the public-peer connection
|
||||
// ceiling and are always dialed — this is the local mesh (the
|
||||
// gateway + sibling peers) and it must never be starved by a
|
||||
// busy public DHT that has pushed `connected` past `target`.
|
||||
// mDNS is LAN-scoped, so the set stays small (no unbounded-dial
|
||||
// risk); remote/public peers still respect the ceiling below.
|
||||
match swarm.dial(*pid) {
|
||||
Ok(()) => debug!(peer_id = %pid, %addr,
|
||||
"mdns: dialing newly-discovered peer"),
|
||||
Err(e) => debug!(peer_id = %pid, %addr, error = %e,
|
||||
"mdns: dial failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2c.5) Bitswap (M12) — fire the reply oneshot for an outbound
|
||||
// `.get(cid)` when bitswap delivers the block or surfaces
|
||||
// a query error. The same Behaviour also serves inbound
|
||||
// WANT-HAVE/WANT-BLOCK from our local blockstore;
|
||||
// those are intercepted by the Behaviour internally and
|
||||
// never bubble up as `beetswap::Event` variants, so this
|
||||
// arm only handles outbound results.
|
||||
if let SwarmEvent::Behaviour(BehaviourEvent::Bitswap(ev)) = &event {
|
||||
let fired = self.bitswap_inflight.on_event(ev);
|
||||
if fired {
|
||||
trace!("bitswap: inflight query resolved");
|
||||
}
|
||||
}
|
||||
// 3) kad providers — dial each new peer-id (subject to soft ceiling).
|
||||
if let SwarmEvent::Behaviour(BehaviourEvent::Kad(
|
||||
kad::Event::OutboundQueryProgressed {
|
||||
result: QueryResult::GetProviders(Ok(GetProvidersOk::FoundProviders {
|
||||
providers, ..
|
||||
})),
|
||||
..
|
||||
}
|
||||
)) = &event {
|
||||
for pid in providers.iter() {
|
||||
if *pid == self.local_peer_id {
|
||||
continue; // ourselves
|
||||
}
|
||||
if swarm.is_connected(pid) {
|
||||
trace!(peer_id = %pid, "kad: provider already connected");
|
||||
continue;
|
||||
}
|
||||
let connected = swarm.connected_peers().count();
|
||||
if connected >= self.kad_target_peer_count {
|
||||
trace!(peer_id = %pid,
|
||||
connected, target = self.kad_target_peer_count,
|
||||
"kad: at target — skipping dial of provider");
|
||||
continue;
|
||||
}
|
||||
match swarm.dial(*pid) {
|
||||
Ok(()) => debug!(peer_id = %pid,
|
||||
"kad: dialing newly-discovered provider"),
|
||||
Err(e) => debug!(peer_id = %pid, error = %e,
|
||||
"kad: dial of discovered provider failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default_log(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic swarm-event logger — catches everything worth surfacing at
|
||||
/// info/debug/trace level after `on_swarm_event`'s explicit branches.
|
||||
fn default_log(event: SwarmEvent<BehaviourEvent>) {
|
||||
match event {
|
||||
SwarmEvent::NewListenAddr { address, .. } => {
|
||||
info!(%address, "swarm listening");
|
||||
}
|
||||
SwarmEvent::ConnectionEstablished { peer_id, .. } => {
|
||||
trace!(%peer_id, "connection established");
|
||||
}
|
||||
SwarmEvent::ConnectionClosed { peer_id, cause, .. } => {
|
||||
trace!(%peer_id, ?cause, "connection closed");
|
||||
}
|
||||
SwarmEvent::IncomingConnectionError { error, .. } => {
|
||||
warn!(%error, "incoming connection error");
|
||||
}
|
||||
SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => {
|
||||
warn!(?peer_id, %error, "outgoing connection error");
|
||||
}
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Identify(identify::Event::Received { peer_id, info, .. })) => {
|
||||
trace!(%peer_id, listen_addrs = info.listen_addrs.len(), "identify received");
|
||||
}
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Identify(_)) => {
|
||||
// identify::Event::{Sent,Pushed,Error} — not interesting at info level
|
||||
}
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Kad(kad::Event::OutboundQueryProgressed {
|
||||
result: QueryResult::GetProviders(Ok(GetProvidersOk::FoundProviders { providers, .. })),
|
||||
..
|
||||
})) => {
|
||||
debug!(provider_count = providers.len(), "kad: GetProviders ok");
|
||||
}
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Kad(kad::Event::OutboundQueryProgressed {
|
||||
result: QueryResult::StartProviding(res), ..
|
||||
})) => match res {
|
||||
Ok(_) => debug!("kad: StartProviding ok"),
|
||||
Err(e) => warn!(error = ?e, "kad: StartProviding error"),
|
||||
},
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Kad(kad::Event::OutboundQueryProgressed {
|
||||
result: QueryResult::Bootstrap(res), ..
|
||||
})) => match res {
|
||||
Ok(_) => trace!("kad: Bootstrap step ok"),
|
||||
Err(e) => debug!(error = ?e, "kad: Bootstrap step error"),
|
||||
},
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Kad(_)) => {
|
||||
// RoutingUpdated, InboundRequest (won't happen in client mode),
|
||||
// ModeChanged, etc. — quiet at info.
|
||||
}
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Mdns(mdns::Event::Discovered(peers))) => {
|
||||
trace!(peer_count = peers.len(), "mdns: discovered local peers");
|
||||
}
|
||||
SwarmEvent::Behaviour(BehaviourEvent::Mdns(mdns::Event::Expired(peers))) => {
|
||||
trace!(peer_count = peers.len(), "mdns: expired local peers");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user