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;
10use url::Url;
11
12use crate::envs::{FM_IROH_ENABLE_DHT_ENV, is_env_var_set};
13use crate::iroh_prod::{FM_IROH_DNS_FEDIMINT_PROD, FM_IROH_RELAYS_FEDIMINT_PROD};
14
15pub async fn build_iroh_endpoint(
16    secret_key: SecretKey,
17    bind_addr: SocketAddr,
18    iroh_dns: Option<SafeUrl>,
19    iroh_relays: Vec<SafeUrl>,
20    alpn: &[u8],
21) -> Result<Endpoint, anyhow::Error> {
22    let iroh_dns_servers: Vec<_> = iroh_dns.clone().map_or_else(
23        || {
24            FM_IROH_DNS_FEDIMINT_PROD
25                .into_iter()
26                .map(|dns| dns.parse().expect("Can't fail"))
27                .collect()
28        },
29        |iroh_dns| vec![iroh_dns.to_unsafe()],
30    );
31
32    let relay_mode = if iroh_relays.is_empty() {
33        RelayMode::Custom(
34            FM_IROH_RELAYS_FEDIMINT_PROD
35                .into_iter()
36                .map(|url| RelayNode {
37                    url: RelayUrl::from(Url::parse(url).expect("Hardcoded, can't fail")),
38                    stun_only: false,
39                    stun_port: DEFAULT_STUN_PORT,
40                    quic: Some(RelayQuicConfig::default()),
41                })
42                .collect(),
43        )
44    } else {
45        RelayMode::Custom(
46            iroh_relays
47                .into_iter()
48                .map(|url| RelayNode {
49                    url: RelayUrl::from(url.to_unsafe()),
50                    stun_only: false,
51                    stun_port: DEFAULT_STUN_PORT,
52                    quic: Some(RelayQuicConfig::default()),
53                })
54                .collect(),
55        )
56    };
57
58    let mut builder = Endpoint::builder();
59
60    for iroh_dns in iroh_dns_servers {
61        builder = builder
62            .add_discovery({
63                let iroh_dns = iroh_dns.clone();
64                move |sk: &SecretKey| Some(PkarrPublisher::new(sk.clone(), iroh_dns))
65            })
66            .add_discovery(|_| Some(PkarrResolver::new(iroh_dns)));
67    }
68
69    // See <https://github.com/fedimint/fedimint/issues/7811>
70    if is_env_var_set(FM_IROH_ENABLE_DHT_ENV) {
71        #[cfg(not(target_family = "wasm"))]
72        {
73            builder = builder.discovery_dht();
74        }
75    } else {
76        info!(
77            target: LOG_NET_IROH,
78            "Iroh DHT is disabled"
79        );
80    }
81
82    let builder = builder
83        .discovery_n0()
84        .relay_mode(relay_mode)
85        .secret_key(secret_key)
86        .alpns(vec![alpn.to_vec()]);
87
88    let builder = match bind_addr {
89        SocketAddr::V4(addr_v4) => builder.bind_addr_v4(addr_v4),
90        SocketAddr::V6(addr_v6) => builder.bind_addr_v6(addr_v6),
91    };
92
93    let endpoint = builder.bind().await.expect("Could not bind to port");
94
95    info!(
96        target: LOG_NET_IROH,
97        %bind_addr,
98        node_id = %endpoint.node_id(),
99        node_id_pkarr = %z32::encode(endpoint.node_id().as_bytes()),
100        "Iroh p2p server endpoint"
101    );
102
103    Ok(endpoint)
104}