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 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}