Skip to main content

fedimint_core/net/
iroh.rs

1use std::net::SocketAddr;
2
3use anyhow::Context;
4use fedimint_core::util::SafeUrl;
5use fedimint_logging::LOG_NET_IROH;
6use iroh::defaults::DEFAULT_STUN_PORT;
7use iroh::discovery::pkarr::{PkarrPublisher, PkarrResolver};
8use iroh::{Endpoint, RelayMode, RelayNode, RelayUrl, SecretKey};
9use iroh_relay::RelayQuicConfig;
10use tracing::{debug, info, warn};
11
12use crate::envs::{
13    FM_IROH_DHT_ENABLE_ENV, FM_IROH_N0_DISCOVERY_ENABLE_ENV, FM_IROH_PKARR_PUBLISHER_ENABLE_ENV,
14    FM_IROH_PKARR_RESOLVER_ENABLE_ENV, FM_IROH_RELAYS_ENABLE_ENV, is_env_var_set,
15    is_env_var_set_opt,
16};
17
18pub async fn build_iroh_endpoint(
19    secret_key: SecretKey,
20    bind_addr: SocketAddr,
21    iroh_dns: Option<SafeUrl>,
22    iroh_relays: Vec<SafeUrl>,
23    alpn: &[u8],
24) -> Result<Endpoint, anyhow::Error> {
25    let relay_mode = if !is_env_var_set_opt(FM_IROH_RELAYS_ENABLE_ENV).unwrap_or(true) {
26        warn!(
27            target: LOG_NET_IROH,
28            "Iroh relays are disabled"
29        );
30        RelayMode::Disabled
31    } else if iroh_relays.is_empty() {
32        RelayMode::Default
33    } else {
34        RelayMode::Custom(
35            iroh_relays
36                .into_iter()
37                .map(|url| RelayNode {
38                    url: RelayUrl::from(url.to_unsafe()),
39                    stun_only: false,
40                    stun_port: DEFAULT_STUN_PORT,
41                    quic: Some(RelayQuicConfig::default()),
42                })
43                .collect(),
44        )
45    };
46
47    let mut builder = Endpoint::builder();
48
49    if let Some(iroh_dns) = iroh_dns.map(SafeUrl::to_unsafe) {
50        if is_env_var_set_opt(FM_IROH_PKARR_PUBLISHER_ENABLE_ENV).unwrap_or(true) {
51            builder = builder.add_discovery({
52                let iroh_dns = iroh_dns.clone();
53                move |sk: &SecretKey| Some(PkarrPublisher::new(sk.clone(), iroh_dns))
54            });
55        } else {
56            warn!(
57                target: LOG_NET_IROH,
58                "Iroh pkarr publisher is disabled"
59            );
60        }
61
62        if is_env_var_set_opt(FM_IROH_PKARR_RESOLVER_ENABLE_ENV).unwrap_or(true) {
63            builder = builder.add_discovery(|_| Some(PkarrResolver::new(iroh_dns)));
64        } else {
65            warn!(
66                target: LOG_NET_IROH,
67                "Iroh pkarr resolver is disabled"
68            );
69        }
70    }
71
72    // See <https://github.com/fedimint/fedimint/issues/7811>
73    if is_env_var_set(FM_IROH_DHT_ENABLE_ENV) {
74        #[cfg(not(target_family = "wasm"))]
75        {
76            debug!(
77                target: LOG_NET_IROH,
78                "Iroh DHT is enabled"
79            );
80            builder = builder.discovery_dht();
81        }
82    } else {
83        info!(
84            target: LOG_NET_IROH,
85            "Iroh DHT is disabled"
86        );
87    }
88
89    if is_env_var_set_opt(FM_IROH_N0_DISCOVERY_ENABLE_ENV).unwrap_or(true) {
90        builder = builder.discovery_n0();
91    } else {
92        warn!(
93            target: LOG_NET_IROH,
94            "Iroh n0 discovery is disabled"
95        );
96    }
97
98    let builder = builder
99        .relay_mode(relay_mode)
100        .secret_key(secret_key)
101        .alpns(vec![alpn.to_vec()]);
102
103    let builder = match bind_addr {
104        SocketAddr::V4(addr_v4) => builder.bind_addr_v4(addr_v4),
105        SocketAddr::V6(addr_v6) => builder.bind_addr_v6(addr_v6),
106    };
107
108    let endpoint = Box::pin(builder.bind())
109        .await
110        .context("Failed to bind Iroh endpoint")?;
111
112    info!(
113        target: LOG_NET_IROH,
114        %bind_addr,
115        node_id = %endpoint.node_id(),
116        node_id_pkarr = %z32::encode(endpoint.node_id().as_bytes()),
117        "Iroh p2p server endpoint"
118    );
119
120    Ok(endpoint)
121}