1use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6#[cfg(all(feature = "browser", target_family = "wasm"))]
7use std::sync::Mutex;
8
9use rings_core::dht::Did;
10use rings_core::dht::EntryStorage;
11#[cfg(feature = "node")]
12use rings_core::lifecycle::StopToken;
13use rings_core::measure::PeerMeasurement;
14use rings_core::session::SessionSkBuilder;
15use rings_core::storage::MemStorage;
16use rings_core::swarm::callback::SharedSwarmCallback;
17use rings_core::swarm::callback::SwarmCallback;
18use rings_rpc::protos::rings_node_handler::InternalRpcHandler;
19
20use crate::error::Error;
21use crate::error::Result;
22use crate::extension::Backend;
23use crate::measure::MeasureStorage;
24use crate::measure::PeriodicMeasure;
25use crate::prelude::wasm_export;
26use crate::processor::Processor;
27use crate::processor::ProcessorBuilder;
28use crate::processor::ProcessorConfig;
29
30#[cfg(all(feature = "browser", target_family = "wasm"))]
31pub mod browser;
32#[cfg(feature = "ffi")]
33pub mod ffi;
34
35#[derive(Clone)]
39#[allow(dead_code)]
40#[repr(C)]
41#[wasm_export]
42pub struct Provider {
43 processor: Arc<Processor>,
44 handler: InternalRpcHandler,
45 extensions: crate::extension::ext::Extensions,
46 #[cfg(all(feature = "browser", target_family = "wasm"))]
47 onion_https_runtime: Arc<Mutex<Option<Arc<crate::onion::https::OnionHttpsRuntime>>>>,
48 #[cfg(all(feature = "browser", target_family = "wasm"))]
49 onion_directory_endpoint: Arc<Mutex<Option<String>>>,
50}
51
52#[cfg(all(feature = "browser", target_family = "wasm"))]
54pub type AsyncSigner = Box<dyn Fn(String) -> Pin<Box<dyn Future<Output = Vec<u8>>>>>;
55
56#[cfg(not(all(feature = "browser", target_family = "wasm")))]
58pub type AsyncSigner = Box<dyn Fn(String) -> Pin<Box<dyn Future<Output = Vec<u8>> + Send>>>;
59
60#[allow(clippy::type_complexity)]
62pub enum Signer {
63 Sync(Box<dyn Fn(String) -> Vec<u8>>),
65 Async(AsyncSigner),
67}
68
69struct NoopSwarmCallback;
70
71impl SwarmCallback for NoopSwarmCallback {}
72
73#[allow(dead_code)]
74impl Provider {
75 pub fn from_processor(processor: Arc<Processor>) -> Self {
77 let extensions = crate::extension::ext::Extensions::new(processor.clone());
78 Self {
79 processor,
80 handler: InternalRpcHandler,
81 extensions,
82 #[cfg(all(feature = "browser", target_family = "wasm"))]
83 onion_https_runtime: Arc::new(Mutex::new(None)),
84 #[cfg(all(feature = "browser", target_family = "wasm"))]
85 onion_directory_endpoint: Arc::new(Mutex::new(None)),
86 }
87 }
88
89 pub fn extensions(&self) -> crate::extension::ext::Extensions {
92 self.extensions.clone()
93 }
94
95 pub(crate) fn core(&self) -> crate::extension::ext::Core {
98 self.extensions.core()
99 }
100
101 pub fn register_protocol<P, I>(&self, protocol: P, interpret: I) -> Result<()>
105 where
106 P: crate::extension::ext::Protocol + crate::extension::ext::MaybeSend + 'static,
107 P::State: crate::extension::ext::MaybeSend + 'static,
108 P::Effect: crate::extension::ext::MaybeSend,
109 I: crate::extension::ext::Interpret<Effect = P::Effect>
110 + crate::extension::ext::MaybeSend
111 + 'static,
112 {
113 self.extensions.register(protocol, interpret)
114 }
115
116 pub async fn send(
119 &self,
120 to: rings_core::dht::Did,
121 namespace: &str,
122 payload: bytes::Bytes,
123 ) -> Result<()> {
124 self.core().send(to, namespace, payload).await
125 }
126
127 pub async fn peer_measurement(&self, did: Did) -> Option<PeerMeasurement> {
129 self.processor.peer_measurement(did).await
130 }
131
132 pub async fn peer_measurements(&self) -> Vec<PeerMeasurement> {
134 self.processor.peer_measurements().await
135 }
136
137 pub(crate) async fn flush_measurements(&self) -> Result<()> {
138 self.processor.flush_measurements().await
139 }
140
141 pub(crate) async fn new_provider_with_storage_internal(
143 config: ProcessorConfig,
144 entry_storage: Option<EntryStorage>,
145 measure_storage: Option<MeasureStorage>,
146 ) -> Result<Provider> {
147 let entry_storage = entry_storage.unwrap_or_else(|| Box::new(MemStorage::new()));
148 let measure_storage = measure_storage.unwrap_or_else(|| Box::new(MemStorage::new()));
149
150 let measure = PeriodicMeasure::new(measure_storage).await?;
151
152 let processor_builder = ProcessorBuilder::from_config(&config)?
153 .storage(entry_storage)
154 .measure(measure);
155
156 let processor = Arc::new(processor_builder.build()?);
157
158 let extensions = crate::extension::ext::Extensions::new(processor.clone());
159
160 Ok(Provider {
161 processor,
162 handler: InternalRpcHandler,
163 extensions,
164 #[cfg(all(feature = "browser", target_family = "wasm"))]
165 onion_https_runtime: Arc::new(Mutex::new(None)),
166 #[cfg(all(feature = "browser", target_family = "wasm"))]
167 onion_directory_endpoint: Arc::new(Mutex::new(None)),
168 })
169 }
170
171 #[allow(clippy::too_many_arguments)]
180 pub(crate) async fn new_provider_internal(
181 network_id: u32,
182 ice_servers: String,
183 stabilize_interval: u64,
184 account: String,
185 account_type: String,
186 signer: Signer,
187 entry_storage: Option<EntryStorage>,
188 measure_storage: Option<MeasureStorage>,
189 ) -> Result<Provider> {
190 Self::new_provider_internal_with_config(
191 network_id,
192 ice_servers,
193 stabilize_interval,
194 account,
195 account_type,
196 signer,
197 entry_storage,
198 measure_storage,
199 core::convert::identity,
200 )
201 .await
202 }
203
204 #[allow(clippy::too_many_arguments)]
205 pub(crate) async fn new_provider_internal_with_config(
206 network_id: u32,
207 ice_servers: String,
208 stabilize_interval: u64,
209 account: String,
210 account_type: String,
211 signer: Signer,
212 entry_storage: Option<EntryStorage>,
213 measure_storage: Option<MeasureStorage>,
214 configure: impl FnOnce(ProcessorConfig) -> ProcessorConfig,
215 ) -> Result<Provider> {
216 let mut sk_builder = SessionSkBuilder::new(account, account_type);
217 let proof = sk_builder.unsigned_proof();
218 let sig = match signer {
219 Signer::Sync(s) => s(proof),
220 Signer::Async(s) => s(proof).await,
221 };
222 sk_builder = sk_builder.set_session_sig(sig.to_vec());
223 let session_sk = sk_builder.build().map_err(Error::InternalError)?;
224 let config = ProcessorConfig::new(network_id, ice_servers, session_sk, stabilize_interval);
225 let config = configure(config);
226 Self::new_provider_with_storage_internal(config, entry_storage, measure_storage).await
227 }
228
229 pub fn set_backend(&self) -> Result<()> {
233 let backend = Backend::new(Arc::new(self.clone()));
234 self.processor
235 .swarm
236 .set_callback(Arc::new(backend))
237 .map_err(Error::InternalError)
238 }
239
240 #[deprecated(
242 note = "set_swarm_callback will be removed in next version, plz use set_backend instead"
243 )]
244 pub fn set_swarm_callback(&self, callback: SharedSwarmCallback) -> Result<()> {
245 self.processor
246 .swarm
247 .set_callback(callback)
248 .map_err(Error::InternalError)
249 }
250
251 pub(crate) fn set_swarm_callback_internal(&self, callback: SharedSwarmCallback) -> Result<()> {
252 self.processor
253 .swarm
254 .set_callback(callback)
255 .map_err(Error::InternalError)
256 }
257
258 pub(crate) fn clear_swarm_callback_internal(&self) -> Result<()> {
259 self.processor
260 .swarm
261 .set_callback(Arc::new(NoopSwarmCallback))
262 .map_err(Error::InternalError)
263 }
264
265 pub async fn request_internal(
268 &self,
269 method: String,
270 params: serde_json::Value,
271 ) -> Result<serde_json::Value> {
272 tracing::debug!("request {}", method);
273 #[cfg(all(feature = "browser", target_family = "wasm"))]
274 let onion_directory_endpoint = onion_directory_endpoint_from_rpc(method.as_str(), ¶ms);
275 let result = self
276 .handler
277 .handle_request(self.processor.clone(), method, params)
278 .await
279 .map_err(Error::InternalRpcError)?;
280 #[cfg(all(feature = "browser", target_family = "wasm"))]
281 if let Some(endpoint) = onion_directory_endpoint {
282 self.set_onion_directory_endpoint(Some(endpoint))?;
283 }
284 Ok(result)
285 }
286}
287
288#[cfg(feature = "node")]
289impl Provider {
290 pub async fn request<T>(
292 &self,
293 method: rings_rpc::method::Method,
294 params: T,
295 ) -> Result<serde_json::Value>
296 where
297 T: serde::Serialize,
298 {
299 let params = serde_json::to_value(params)?;
300 self.request_internal(method.to_string(), params).await
301 }
302
303 pub async fn listen(&self) {
307 self.processor.listen().await;
308 }
309
310 pub async fn listen_with(&self, stop: StopToken) {
314 self.processor.listen_with(stop).await;
315 }
316}
317
318#[cfg(all(feature = "browser", target_family = "wasm"))]
319fn onion_directory_endpoint_from_rpc(method: &str, params: &serde_json::Value) -> Option<String> {
320 if !matches!(method, "connectPeerViaHttp" | "ConnectPeerViaHttp") {
321 return None;
322 }
323 params
324 .get("url")
325 .and_then(serde_json::Value::as_str)
326 .map(str::trim)
327 .filter(|url| !url.is_empty())
328 .map(ToOwned::to_owned)
329}
330
331#[cfg(all(feature = "browser", target_family = "wasm"))]
332impl Provider {
333 pub(crate) fn set_onion_directory_endpoint(&self, endpoint: Option<String>) -> Result<()> {
334 let mut slot = self
335 .onion_directory_endpoint
336 .lock()
337 .map_err(|_| Error::Lock)?;
338 *slot = endpoint;
339 Ok(())
340 }
341
342 pub(crate) fn onion_directory_endpoint(&self) -> Result<Option<String>> {
343 self.onion_directory_endpoint
344 .lock()
345 .map(|slot| slot.clone())
346 .map_err(|_| Error::Lock)
347 }
348}