p2p/service_impl/
webrtc_with_libp2p.rs

1use std::collections::BTreeSet;
2
3use crate::{
4    channels::{ChannelId, ChannelMsg, MsgId, P2pChannelsService},
5    connection::{outgoing::P2pConnectionOutgoingInitOpts, P2pConnectionService},
6    disconnection_effectful::P2pDisconnectionService,
7    identity::{PublicKey, SecretKey},
8    webrtc::{ConnectionAuth, ConnectionAuthEncrypted},
9    P2pChannelEvent, P2pEvent, PeerId,
10};
11
12#[cfg(feature = "p2p-libp2p")]
13use super::mio::MioService;
14#[cfg(feature = "p2p-libp2p")]
15use crate::{P2pMioService, P2pNetworkService, P2pNetworkServiceError};
16
17use super::{webrtc::P2pServiceWebrtc, TaskSpawner};
18
19pub struct P2pServiceCtx {
20    pub sec_key: SecretKey,
21    pub webrtc: super::webrtc::P2pServiceCtx,
22    #[cfg(feature = "p2p-libp2p")]
23    pub mio: MioService,
24}
25
26pub trait P2pServiceWebrtcWithLibp2p: P2pServiceWebrtc {
27    #[cfg(feature = "p2p-libp2p")]
28    fn mio(&mut self) -> &mut MioService;
29
30    fn connections(&self) -> BTreeSet<PeerId>;
31
32    fn init<S: TaskSpawner>(sec_key: SecretKey, spawner: S, rng_seed: [u8; 32]) -> P2pServiceCtx {
33        P2pServiceCtx {
34            sec_key: sec_key.clone(),
35            #[cfg(feature = "p2p-libp2p")]
36            mio: MioService::pending(sec_key.clone().try_into().expect("valid keypair")),
37            webrtc: <Self as P2pServiceWebrtc>::init(sec_key, spawner, rng_seed),
38        }
39    }
40
41    #[cfg(feature = "p2p-libp2p")]
42    fn resolve_name(
43        &mut self,
44        hostname: &str,
45    ) -> Result<Vec<std::net::IpAddr>, P2pNetworkServiceError> {
46        use std::net::ToSocketAddrs;
47
48        let it = format!("{hostname}:0")
49            .to_socket_addrs()
50            .map_err(|err| P2pNetworkServiceError::Resolve(format!("{hostname}, {err}")))?;
51        Ok(it.map(|addr| addr.ip()).collect())
52    }
53
54    #[cfg(feature = "p2p-libp2p")]
55    fn detect_local_ip(&mut self) -> Result<Vec<std::net::IpAddr>, P2pNetworkServiceError> {
56        let addrs = local_ip_address::list_afinet_netifas()
57            .map_err(|e| P2pNetworkServiceError::LocalIp(e.to_string()))?;
58        Ok(addrs.into_iter().map(|(_, ip)| ip).collect())
59    }
60}
61
62#[cfg(feature = "p2p-libp2p")]
63impl<T: P2pServiceWebrtcWithLibp2p> P2pNetworkService for T {
64    fn resolve_name(
65        &mut self,
66        host: &str,
67    ) -> Result<Vec<std::net::IpAddr>, P2pNetworkServiceError> {
68        P2pServiceWebrtcWithLibp2p::resolve_name(self, host)
69    }
70
71    fn detect_local_ip(&mut self) -> Result<Vec<std::net::IpAddr>, P2pNetworkServiceError> {
72        P2pServiceWebrtcWithLibp2p::detect_local_ip(self)
73    }
74}
75
76impl<T: P2pServiceWebrtcWithLibp2p> P2pConnectionService for T {
77    fn connections(&self) -> std::collections::BTreeSet<PeerId> {
78        P2pServiceWebrtcWithLibp2p::connections(self)
79    }
80
81    fn random_pick(
82        &mut self,
83        list: &[P2pConnectionOutgoingInitOpts],
84    ) -> Option<P2pConnectionOutgoingInitOpts> {
85        P2pServiceWebrtc::random_pick(self, list)
86    }
87
88    fn outgoing_init(&mut self, opts: P2pConnectionOutgoingInitOpts) {
89        match opts {
90            P2pConnectionOutgoingInitOpts::WebRTC { peer_id, .. } => {
91                P2pServiceWebrtc::outgoing_init(self, peer_id);
92            }
93            #[cfg(not(feature = "p2p-libp2p"))]
94            P2pConnectionOutgoingInitOpts::LibP2P(_) => {}
95            #[cfg(feature = "p2p-libp2p")]
96            P2pConnectionOutgoingInitOpts::LibP2P(opts) => {
97                use crate::webrtc::Host;
98                let addr = match opts.host {
99                    Host::Ipv4(ip4) => ip4.into(),
100                    Host::Ipv6(ip6) => ip6.into(),
101                    host => {
102                        openmina_core::error!(openmina_core::log::system_time(); "unsupported host for internal libp2p: {host}");
103                        return;
104                    }
105                };
106                self.mio()
107                    .send_cmd(crate::MioCmd::Connect(std::net::SocketAddr::new(
108                        addr, opts.port,
109                    )));
110            }
111        }
112    }
113
114    fn incoming_init(&mut self, peer_id: PeerId, offer: crate::webrtc::Offer) {
115        P2pServiceWebrtc::incoming_init(self, peer_id, offer)
116    }
117
118    fn set_answer(&mut self, peer_id: PeerId, answer: crate::webrtc::Answer) {
119        P2pServiceWebrtc::set_answer(self, peer_id, answer)
120    }
121
122    fn http_signaling_request(&mut self, url: String, offer: crate::webrtc::Offer) {
123        P2pServiceWebrtc::http_signaling_request(self, url, offer)
124    }
125
126    fn auth_encrypt_and_send(
127        &mut self,
128        peer_id: PeerId,
129        other_pub_key: &PublicKey,
130        auth: ConnectionAuth,
131    ) {
132        P2pServiceWebrtc::auth_encrypt_and_send(self, peer_id, other_pub_key, auth)
133    }
134
135    fn auth_decrypt(
136        &mut self,
137        other_pub_key: &PublicKey,
138        auth: ConnectionAuthEncrypted,
139    ) -> Option<ConnectionAuth> {
140        P2pServiceWebrtc::auth_decrypt(self, other_pub_key, auth)
141    }
142}
143
144impl<T: P2pServiceWebrtcWithLibp2p> P2pDisconnectionService for T {
145    fn disconnect(&mut self, peer_id: PeerId) -> bool {
146        P2pServiceWebrtc::disconnect(self, peer_id)
147    }
148}
149
150impl<T: P2pServiceWebrtcWithLibp2p> P2pChannelsService for T {
151    fn channel_open(&mut self, peer_id: PeerId, id: ChannelId) {
152        if self.peers().contains_key(&peer_id) {
153            P2pServiceWebrtc::channel_open(self, peer_id, id)
154        } else if !matches!(id, ChannelId::Rpc) {
155            // skip sending event for rpc libp2p channel as the ready
156            // action is dispatched in the `network` module after the
157            // relevant handshake is done.
158            // TODO: do the same for other channels/streams also.
159            let result = match id.supported_by_libp2p() {
160                false => Err("channel not supported".to_owned()),
161                true => Ok(()),
162            };
163            self.event_sender()
164                .send(P2pEvent::Channel(P2pChannelEvent::Opened(peer_id, id, result)).into())
165                .unwrap_or_default();
166        }
167    }
168
169    fn channel_send(&mut self, peer_id: PeerId, msg_id: MsgId, msg: ChannelMsg) {
170        if self.peers().contains_key(&peer_id) {
171            P2pServiceWebrtc::channel_send(self, peer_id, msg_id, msg)
172        }
173    }
174
175    fn encrypt<M: crate::identity::EncryptableType>(
176        &mut self,
177        other_pk: &crate::identity::PublicKey,
178        message: &M,
179    ) -> Result<M::Encrypted, Box<dyn std::error::Error>> {
180        P2pServiceWebrtc::encrypt(self, other_pk, message)
181    }
182
183    fn decrypt<M: crate::identity::EncryptableType>(
184        &mut self,
185        other_pk: &crate::identity::PublicKey,
186        encrypted: &M::Encrypted,
187    ) -> Result<M, Box<dyn std::error::Error>> {
188        P2pServiceWebrtc::decrypt(self, other_pk, encrypted)
189    }
190}
191
192#[cfg(feature = "p2p-libp2p")]
193impl<T> P2pMioService for T
194where
195    T: P2pServiceWebrtcWithLibp2p,
196{
197    #[cfg(feature = "p2p-libp2p")]
198    fn start_mio(&mut self) {
199        let event_sender = self.event_sender().clone();
200        self.mio().run(move |mio_event| {
201            event_sender
202                .send(P2pEvent::MioEvent(mio_event).into())
203                .unwrap_or_default()
204        });
205    }
206
207    #[cfg(feature = "p2p-libp2p")]
208    fn send_mio_cmd(&mut self, cmd: crate::MioCmd) {
209        self.mio().send_cmd(cmd)
210    }
211}
212
213impl P2pServiceCtx {
214    pub fn mocked(sec_key: SecretKey) -> Self {
215        use openmina_core::channels::mpsc;
216        Self {
217            sec_key: sec_key.clone(),
218            #[cfg(feature = "p2p-libp2p")]
219            mio: super::mio::MioService::mocked(sec_key.try_into().expect("valid keypair")),
220            webrtc: super::webrtc::P2pServiceCtx {
221                cmd_sender: mpsc::unbounded_channel().0,
222                peers: Default::default(),
223            },
224        }
225    }
226}