fedimint_core/net/
iroh.rs

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