fedimint_core/net/
iroh.rs1use 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 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}