1use std::num::NonZeroUsize;
4use std::str::FromStr;
5use std::sync::Arc;
6use std::time::Duration;
7
8use futures::future::join_all;
9use rings_core::chunk::ReassemblyLimits;
10use rings_core::dht::Did;
11use rings_core::dht::EntryStorage;
12use rings_core::dht::DEFAULT_FINGER_TABLE_SIZE;
13use rings_core::ecc::PublicKey;
14use rings_core::ecc::SecretKey;
15use rings_core::lifecycle::StopSource;
16use rings_core::lifecycle::StopToken;
17use rings_core::measure::MeasureImpl;
18use rings_core::measure::PeerMeasurement;
19use rings_core::measure::PeerMeasurementPage;
20use rings_core::measure::PeerQuality;
21use rings_core::message::e2e;
22use rings_core::message::e2e::E2eHandshakeRequest;
23use rings_core::message::e2e::E2eHandshakeResponse;
24use rings_core::message::e2e::E2eStreamDecryptor;
25use rings_core::message::e2e::E2eStreamFrame;
26use rings_core::message::DhtProtocolMode;
27use rings_core::message::Encoded;
28use rings_core::message::Encoder;
29use rings_core::message::Message;
30use rings_core::storage::MemStorage;
31use rings_core::swarm::Swarm;
32use rings_core::swarm::SwarmBuilder;
33use rings_core::utils::get_epoch_ms;
34use rings_rpc::protos::rings_node::*;
35use rings_transport::webrtc_config::WebrtcUdpPortRange;
36use serde::Deserialize;
37use serde::Serialize;
38use uuid;
39
40use crate::consts::DATA_REDUNDANT;
41use crate::error::Error;
42use crate::error::Result;
43use crate::measure::PeriodicMeasure;
44use crate::onion::default_advertise_onion_exit;
45use crate::onion::default_advertise_onion_relay;
46use crate::onion::default_onion_exit_heartbeat_interval_secs;
47use crate::onion::default_onion_exit_policy;
48use crate::onion::default_onion_exit_services;
49use crate::onion::default_onion_exit_ttl_secs;
50use crate::onion::directory;
51use crate::onion::directory::OnionDirectoryReader;
52use crate::onion::https_onion_exit_services;
53use crate::onion::proxy::OnionProxyConfig;
54use crate::onion::proxy::OnionProxyRoute;
55use crate::onion::proxy::OnionProxyTarget;
56#[cfg(all(feature = "browser", target_family = "wasm"))]
57use crate::onion::proxy::ONION_PROXY_HTTPS_SERVICE;
58use crate::onion::validate_onion_exit_registration_timing;
59use crate::onion::OnionExitDescriptor;
60use crate::onion::OnionExitPolicy;
61use crate::onion::OnionExitRegistration;
62use crate::onion::OnionExitService;
63use crate::onion::OnionRoute;
64use crate::onion::ONION_EXITS_TOPIC;
65use crate::onion::ONION_RELAY_CAPABILITY;
66use crate::online::OnlineNodeDescriptor;
67use crate::online::OnlineNodeType;
68use crate::online::ONLINE_NODES_TOPIC;
69use crate::prelude::entry;
70use crate::prelude::wasm_export;
71use crate::prelude::ChordStorageInterface;
72use crate::prelude::ChordStorageInterfaceCacheChecker;
73use crate::prelude::SessionSk;
74use crate::registration::default_advertise_presence;
75use crate::registration::default_online_node_heartbeat_interval_secs;
76use crate::registration::default_online_node_ttl_secs;
77use crate::registration::default_online_node_type;
78use crate::registration::sleep_registration_interval;
79use crate::registration::validate_online_node_registration_timing;
80use crate::registration::OnlineNodeRegistration;
81use crate::registration::RegistrationContext;
82use crate::registration::RegistrationTask;
83
84const MEASUREMENT_FLUSH_TIMEOUT: Duration = Duration::from_secs(5);
85
86mod builder;
87mod config;
88
89pub use builder::ProcessorBuilder;
90#[cfg(feature = "node")]
91pub(crate) use config::parse_webrtc_udp_port_range;
92pub use config::ProcessorConfig;
93pub use config::ProcessorConfigSerialized;
94
95const DHT_LOOKUP_CACHE_POLL_INTERVAL: Duration = Duration::from_millis(50);
96const DHT_LOOKUP_CACHE_POLL_ATTEMPTS: usize = 40;
97const REGISTRATION_STOP_POLL_INTERVAL: Duration = Duration::from_millis(50);
98
99#[cfg(not(all(feature = "browser", target_family = "wasm")))]
100async fn sleep_dht_lookup_poll_interval(interval: Duration) -> Result<()> {
101 futures_timer::Delay::new(interval).await;
102 Ok(())
103}
104
105#[cfg(all(feature = "browser", target_family = "wasm"))]
106async fn sleep_dht_lookup_poll_interval(interval: Duration) -> Result<()> {
107 let interval_ms = i32::try_from(interval.as_millis()).unwrap_or(i32::MAX);
108 rings_core::utils::js_utils::window_sleep(interval_ms)
109 .await
110 .map_err(|error| Error::JsError(format!("{error:?}")))?;
111 Ok(())
112}
113
114async fn sleep_registration_interval_with_stop(
115 interval: Duration,
116 stop: &StopToken,
117 sibling_stop: &StopToken,
118) -> Result<bool> {
119 let mut remaining = interval;
120 while !remaining.is_zero() {
121 if stop.should_stop() || sibling_stop.should_stop() {
122 return Ok(false);
123 }
124 let step = std::cmp::min(remaining, REGISTRATION_STOP_POLL_INTERVAL);
125 sleep_registration_interval(step).await?;
126 remaining = remaining.saturating_sub(step);
127 }
128 Ok(!(stop.should_stop() || sibling_stop.should_stop()))
129}
130
131#[derive(Clone)]
136pub struct Processor {
137 pub swarm: Arc<Swarm>,
139 session_sk: SessionSk,
141 stabilize_interval: Duration,
142 online_node_registration: OnlineNodeRegistration,
143 measure: Option<Arc<PeriodicMeasure>>,
144 #[cfg(all(feature = "browser", target_family = "wasm"))]
145 advertise_onion_relay: bool,
146 registration_tasks: Vec<Arc<dyn RegistrationTask>>,
147}
148
149impl Processor {
150 pub fn did(&self) -> Did {
152 self.swarm.did()
153 }
154
155 pub(crate) fn session_sk(&self) -> &SessionSk {
156 &self.session_sk
157 }
158
159 #[cfg(all(feature = "browser", target_family = "wasm"))]
160 pub(crate) fn advertise_onion_relay(&self) -> bool {
161 self.advertise_onion_relay
162 }
163
164 fn registration_context(&self) -> RegistrationContext<'_> {
165 RegistrationContext::new(self)
166 }
167
168 fn registration_context_with_stop(&self, stop: StopToken) -> RegistrationContext<'_> {
169 RegistrationContext::new_with_stop(self, stop)
170 }
171
172 pub(crate) fn add_online_node_capabilities<I>(&self, capabilities: I) -> Result<()>
173 where I: IntoIterator<Item = &'static str> {
174 self.online_node_registration.add_capabilities(capabilities)
175 }
176
177 #[cfg(all(test, feature = "node"))]
178 fn online_node_descriptor_at(&self, now_ms: u128) -> Result<OnlineNodeDescriptor> {
179 self.online_node_registration
180 .descriptor_at(&self.registration_context(), now_ms)
181 }
182
183 fn online_node_descriptors_from_entry(entry: &entry::Entry) -> Vec<OnlineNodeDescriptor> {
184 OnlineNodeRegistration::descriptors_from_entry(entry)
185 }
186
187 fn onion_exit_descriptors_from_entry(entry: &entry::Entry) -> Vec<OnionExitDescriptor> {
188 OnionExitRegistration::descriptors_from_entry(entry)
189 }
190
191 #[cfg(all(test, feature = "node"))]
192 fn online_node_registry_entry(descriptors: Vec<OnlineNodeDescriptor>) -> Result<entry::Entry> {
193 let data = descriptors
194 .into_iter()
195 .map(|descriptor| descriptor.encode().map_err(Error::CoreError))
196 .collect::<Result<Vec<_>>>()?;
197
198 Ok(entry::Entry::new(
199 entry::Entry::gen_did(ONLINE_NODES_TOPIC)?,
200 data,
201 entry::EntryKind::Data,
202 ))
203 }
204
205 #[cfg(all(test, feature = "node"))]
206 fn onion_exit_registry_entry(descriptors: Vec<OnionExitDescriptor>) -> Result<entry::Entry> {
207 let data = descriptors
208 .into_iter()
209 .map(|descriptor| descriptor.encode().map_err(Error::CoreError))
210 .collect::<Result<Vec<_>>>()?;
211
212 Ok(entry::Entry::new(
213 entry::Entry::gen_did(ONION_EXITS_TOPIC)?,
214 data,
215 entry::EntryKind::Data,
216 ))
217 }
218
219 pub async fn publish_online_node_descriptor(&self) -> Result<OnlineNodeDescriptor> {
221 self.online_node_registration
222 .publish_descriptor(&self.registration_context())
223 .await
224 }
225
226 pub async fn lookup_online_nodes(
228 &self,
229 include_expired: bool,
230 ) -> Result<Vec<OnlineNodeDescriptor>> {
231 let entry_key = entry::Entry::gen_did(ONLINE_NODES_TOPIC)?;
232
233 let Some(entry) = self.fetch_storage_entry(entry_key).await? else {
234 return Ok(vec![]);
235 };
236
237 let descriptors = Self::online_node_descriptors_from_entry(&entry)
238 .into_iter()
239 .filter(|descriptor| descriptor.matches_dht_protocol(self.swarm.dht_protocol_mode()));
240
241 Ok(OnlineNodeDescriptor::latest_valid_by_did(
242 descriptors,
243 get_epoch_ms(),
244 include_expired,
245 ))
246 }
247
248 pub async fn lookup_onion_exits(
250 &self,
251 service: &str,
252 include_expired: bool,
253 ) -> Result<Vec<OnionExitDescriptor>> {
254 let entry_key = entry::Entry::gen_did(ONION_EXITS_TOPIC)?;
255
256 let Some(entry) = self.fetch_storage_entry(entry_key).await? else {
257 return Ok(vec![]);
258 };
259
260 let service = service.trim();
261 let exits = self.select_onion_exits_from_entry(&entry, service, include_expired);
262 if include_expired
263 || !exits.is_empty()
264 || !self.entry_has_expired_onion_exit_service(&entry, service)
265 {
266 return Ok(exits);
267 }
268
269 let Some(refreshed_entry) = self
270 .fetch_storage_entry_after_cache_refresh(entry_key, &entry)
271 .await?
272 else {
273 return Ok(exits);
274 };
275 Ok(self.select_onion_exits_from_entry(&refreshed_entry, service, include_expired))
276 }
277
278 pub(crate) async fn fetch_storage_entry(&self, entry_key: Did) -> Result<Option<entry::Entry>> {
279 let stop = StopToken::never();
280 self.fetch_storage_entry_with_stop(entry_key, &stop).await
281 }
282
283 pub(crate) async fn fetch_storage_entry_with_stop(
284 &self,
285 entry_key: Did,
286 stop: &StopToken,
287 ) -> Result<Option<entry::Entry>> {
288 if stop.should_stop() {
289 return Err(Error::RegistrationStopped);
290 }
291 self.storage_fetch(entry_key).await?;
292 for attempt in 0..DHT_LOOKUP_CACHE_POLL_ATTEMPTS {
293 if stop.should_stop() {
294 return Err(Error::RegistrationStopped);
295 }
296 if let Some(entry) = self.storage_check_cache(entry_key).await {
297 return Ok(Some(entry));
298 }
299 if attempt + 1 == DHT_LOOKUP_CACHE_POLL_ATTEMPTS {
300 break;
301 }
302 sleep_dht_lookup_poll_interval(DHT_LOOKUP_CACHE_POLL_INTERVAL).await?;
303 }
304 Ok(None)
305 }
306
307 fn select_onion_exits_from_entry(
308 &self,
309 entry: &entry::Entry,
310 service: &str,
311 include_expired: bool,
312 ) -> Vec<OnionExitDescriptor> {
313 OnionExitDescriptor::latest_valid_by_service_did(
314 Self::onion_exit_descriptors_from_entry(entry)
315 .into_iter()
316 .filter(|descriptor| descriptor.matches_network(self.swarm.network_id())),
317 get_epoch_ms(),
318 include_expired,
319 )
320 .into_iter()
321 .filter(|descriptor| service.is_empty() || descriptor.offers_service(service))
322 .collect()
323 }
324
325 fn entry_has_expired_onion_exit_service(&self, entry: &entry::Entry, service: &str) -> bool {
326 let now_ms = get_epoch_ms();
327 Self::onion_exit_descriptors_from_entry(entry)
328 .into_iter()
329 .filter(|descriptor| descriptor.matches_network(self.swarm.network_id()))
330 .any(|descriptor| {
331 (service.is_empty() || descriptor.offers_service(service))
332 && descriptor.verify_signature()
333 && descriptor.is_expired_at(now_ms)
334 })
335 }
336
337 async fn fetch_storage_entry_after_cache_refresh(
338 &self,
339 entry_key: Did,
340 previous_entry: &entry::Entry,
341 ) -> Result<Option<entry::Entry>> {
342 self.storage_fetch(entry_key).await?;
343 for _ in 0..DHT_LOOKUP_CACHE_POLL_ATTEMPTS {
344 sleep_dht_lookup_poll_interval(DHT_LOOKUP_CACHE_POLL_INTERVAL).await?;
345 let Some(entry) = self.storage_check_cache(entry_key).await else {
346 continue;
347 };
348 if &entry != previous_entry {
349 return Ok(Some(entry));
350 }
351 }
352 Ok(self.storage_check_cache(entry_key).await)
353 }
354
355 pub async fn build_onion_route(
357 &self,
358 service: String,
359 hop_count: usize,
360 allow_short_paths: bool,
361 ) -> Result<OnionRoute> {
362 directory::build_onion_route(self, service, hop_count, allow_short_paths).await
363 }
364
365 pub async fn build_onion_proxy_route(
367 &self,
368 proxy: OnionProxyConfig,
369 target: OnionProxyTarget,
370 ) -> Result<OnionProxyRoute> {
371 directory::build_onion_proxy_route(self, proxy, target).await
372 }
373
374 async fn run_registration_once(
375 &self,
376 task: &dyn RegistrationTask,
377 stop: StopToken,
378 ) -> Result<()> {
379 let context = self.registration_context_with_stop(stop);
380 task.register_once(&context).await
381 }
382
383 async fn registration_task_daemon_with(
384 &self,
385 task: &dyn RegistrationTask,
386 stop: StopToken,
387 sibling_stop: StopToken,
388 ) {
389 loop {
390 if stop.should_stop() || sibling_stop.should_stop() {
391 return;
392 }
393 if let Err(error) = self.run_registration_once(task, stop.clone()).await {
394 if matches!(error, Error::RegistrationStopped) {
395 tracing::debug!(
396 "Stopping {} registration task after cooperative stop",
397 task.name()
398 );
399 return;
400 }
401 tracing::warn!("Failed to run {} registration task: {error:?}", task.name());
402 }
403 if stop.should_stop() || sibling_stop.should_stop() {
404 return;
405 }
406 match sleep_registration_interval_with_stop(task.interval(), &stop, &sibling_stop).await
407 {
408 Ok(true) => {}
409 Ok(false) => return,
410 Err(error) => {
411 tracing::warn!(
412 "Stopping {} registration task after timer error: {error:?}",
413 task.name()
414 );
415 return;
416 }
417 }
418 }
419 }
420
421 async fn registration_daemons_with(&self, stop: StopToken, sibling_stop: StopToken) {
422 join_all(self.registration_tasks.iter().map(|task| {
423 self.registration_task_daemon_with(task.as_ref(), stop.clone(), sibling_stop.clone())
424 }))
425 .await;
426 }
427
428 pub async fn listen(&self) {
432 self.listen_with(StopToken::never()).await;
433 }
434
435 pub async fn listen_with(&self, stop: StopToken) {
442 let stabilizer = self.swarm.stabilizer();
443 let stabilizer = Arc::new(stabilizer);
444 if self.registration_tasks.is_empty() {
445 stabilizer.wait_with(self.stabilize_interval, stop).await;
446 } else {
447 let registration_stop_source = StopSource::new();
448 let registration_stop = registration_stop_source.token();
449 let stabilizer_stop = stop.clone();
450 let stabilization = async {
451 stabilizer
452 .wait_with(self.stabilize_interval, stabilizer_stop)
453 .await;
454 registration_stop_source.request_stop();
455 };
456 let _ = futures::future::join(
457 stabilization,
458 self.registration_daemons_with(stop, registration_stop),
459 )
460 .await;
461 }
462 if let Err(error) = self.flush_measurements().await {
463 tracing::error!(%error, "failed to flush measurements during graceful shutdown");
464 }
465 }
466
467 pub async fn flush_measurements(&self) -> Result<()> {
469 if let Some(measure) = &self.measure {
470 measure
471 .flush_with_timeout(MEASUREMENT_FLUSH_TIMEOUT)
472 .await?;
473 }
474 Ok(())
475 }
476
477 #[cfg(all(test, feature = "ffi"))]
478 pub(crate) async fn record_authenticated_measurement_for_test(
479 &self,
480 peer: Did,
481 ) -> std::result::Result<(), rings_core::measure::MeasureError> {
482 if let Some(measure) = &self.measure {
483 rings_core::measure::Measure::record(
484 measure.as_ref(),
485 peer,
486 rings_core::measure::Authentication::Authenticated,
487 rings_core::measure::MeasurementEvent::Connected,
488 )
489 .await?;
490 }
491 Ok(())
492 }
493
494 pub async fn connect_with_did(&self, did: Did) -> Result<()> {
503 match self.swarm.connect(did).await {
504 Ok(()) | Err(rings_core::error::Error::AlreadyConnected) => Ok(()),
505 Err(error) => Err(Error::ConnectError(error)),
506 }
507 }
508
509 pub async fn disconnect(&self, did: Did) -> Result<()> {
511 self.swarm
512 .disconnect(did)
513 .await
514 .map_err(Error::CloseConnectionError)
515 }
516
517 pub async fn send_message(&self, destination: Did, msg: &[u8]) -> Result<uuid::Uuid> {
519 tracing::trace!("send_message, message size: {:?}", msg.len());
520
521 let msg = Message::custom(msg).map_err(Error::SendMessage)?;
522
523 self.swarm
524 .send_message(msg, destination)
525 .await
526 .map_err(Error::SendMessage)
527 }
528
529 pub async fn send_direct_message(&self, destination: Did, msg: &[u8]) -> Result<uuid::Uuid> {
534 tracing::trace!("send_direct_message, message size: {:?}", msg.len());
535
536 let msg = Message::custom(msg).map_err(Error::SendMessage)?;
537
538 self.swarm
539 .send_direct_message(msg, destination)
540 .await
541 .map_err(Error::SendMessage)
542 }
543
544 pub async fn send_e2e_handshake(&self, destination: Did) -> Result<uuid::Uuid> {
549 let public_key = self.swarm.account_pubkey().map_err(Error::SendMessage)?;
550 self.swarm
551 .send_message(
552 Message::E2eHandshakeRequest(E2eHandshakeRequest::new(public_key)),
553 destination,
554 )
555 .await
556 .map_err(Error::SendMessage)
557 }
558
559 pub async fn send_e2e_message(
563 &self,
564 destination: Did,
565 recipient_public_key: PublicKey<33>,
566 msg: &[u8],
567 ) -> Result<uuid::Uuid> {
568 self.send_e2e_message_with_frame_len(
569 destination,
570 recipient_public_key,
571 msg,
572 e2e::DEFAULT_E2E_PLAINTEXT_FRAME_LEN,
573 )
574 .await
575 }
576
577 pub async fn send_e2e_message_with_frame_len(
581 &self,
582 destination: Did,
583 recipient_public_key: PublicKey<33>,
584 msg: &[u8],
585 max_plaintext_frame_len: usize,
586 ) -> Result<uuid::Uuid> {
587 e2e::ensure_public_key_matches_did(recipient_public_key, destination)
588 .map_err(Error::SendMessage)?;
589 let sender_public_key = self.swarm.account_pubkey().map_err(Error::SendMessage)?;
590 let stream_id = uuid::Uuid::new_v4();
591 let frames = e2e::encrypt_stream_frames(
592 msg,
593 stream_id,
594 sender_public_key,
595 recipient_public_key,
596 max_plaintext_frame_len,
597 )
598 .map_err(Error::SendMessage)?
599 .collect::<rings_core::error::Result<Vec<_>>>()
600 .map_err(Error::SendMessage)?;
601
602 for frame in frames {
603 self.swarm
604 .send_message(Message::E2eStreamFrame(frame), destination)
605 .await
606 .map_err(Error::SendMessage)?;
607 }
608
609 Ok(stream_id)
610 }
611
612 pub fn verify_e2e_handshake_request(
614 &self,
615 requester: Did,
616 request: &E2eHandshakeRequest,
617 ) -> Result<PublicKey<33>> {
618 request
619 .verify_requester(requester)
620 .map_err(Error::CoreError)?;
621 Ok(request.requester_public_key)
622 }
623
624 pub fn verify_e2e_handshake_response(
626 &self,
627 responder: Did,
628 response: &E2eHandshakeResponse,
629 ) -> Result<PublicKey<33>> {
630 response
631 .verify_responder(responder)
632 .map_err(Error::CoreError)?;
633 Ok(response.responder_public_key)
634 }
635
636 pub fn e2e_stream_decryptor(
643 &self,
644 expected_sender: Did,
645 stream_id: e2e::E2eStreamId,
646 recipient_identity_key: SecretKey,
647 ) -> Result<E2eStreamDecryptor> {
648 e2e::ensure_public_key_matches_did(recipient_identity_key.pubkey(), self.did())
649 .map_err(Error::CoreError)?;
650 Ok(E2eStreamDecryptor::new(
651 stream_id,
652 expected_sender,
653 recipient_identity_key,
654 ))
655 }
656
657 pub fn decrypt_e2e_stream_frame(
659 &self,
660 decryptor: &mut E2eStreamDecryptor,
661 frame: &E2eStreamFrame,
662 ) -> Result<Vec<u8>> {
663 decryptor.decrypt_next(frame).map_err(Error::CoreError)
664 }
665
666 pub async fn send_envelope(
670 &self,
671 destination: Did,
672 envelope: &crate::extension::ext::Envelope,
673 ) -> Result<uuid::Uuid> {
674 let msg_bytes = envelope.encode()?;
675 self.send_message(destination, &msg_bytes).await
676 }
677
678 pub async fn send_direct_envelope(
682 &self,
683 destination: Did,
684 envelope: &crate::extension::ext::Envelope,
685 ) -> Result<uuid::Uuid> {
686 let msg_bytes = envelope.encode()?;
687 self.send_direct_message(destination, &msg_bytes).await
688 }
689
690 pub async fn storage_check_cache(&self, entry_key: Did) -> Option<entry::Entry> {
692 self.swarm.storage_check_cache(entry_key).await
693 }
694
695 pub async fn storage_fetch(&self, entry_key: Did) -> Result<()> {
697 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_fetch(&self.swarm, entry_key)
698 .await
699 .map_err(Error::EntryError)
700 }
701
702 pub async fn storage_store(&self, entry: entry::Entry) -> Result<()> {
704 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_store(&self.swarm, entry)
705 .await
706 .map_err(Error::EntryError)
707 }
708
709 pub async fn storage_append_data(&self, topic: &str, data: Encoded) -> Result<()> {
711 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_append_data(
712 &self.swarm,
713 topic,
714 data,
715 )
716 .await
717 .map_err(Error::EntryError)
718 }
719
720 pub async fn storage_touch_data(&self, topic: &str, data: Encoded) -> Result<()> {
722 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_touch_data(
723 &self.swarm,
724 topic,
725 data,
726 )
727 .await
728 .map_err(Error::EntryError)
729 }
730
731 pub async fn storage_tombstone_data(&self, topic: &str, data: Encoded) -> Result<()> {
733 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_tombstone_data(
734 &self.swarm,
735 topic,
736 data,
737 )
738 .await
739 .map_err(Error::EntryError)
740 }
741
742 pub async fn storage_compact_data(&self, topic: &str, removals: Vec<Encoded>) -> Result<()> {
744 <Swarm as ChordStorageInterface<DATA_REDUNDANT>>::storage_compact_data(
745 &self.swarm,
746 topic,
747 removals,
748 )
749 .await
750 .map_err(Error::EntryError)
751 }
752
753 pub async fn peer_measurement(&self, did: Did) -> Option<PeerMeasurement> {
755 self.swarm.peer_measurement(did).await
756 }
757
758 pub async fn peer_measurements(&self) -> Vec<PeerMeasurement> {
760 let mut measurements = self.swarm.peer_measurements().await;
761 measurements.sort_by_key(|measurement| measurement.did);
762 measurements
763 }
764
765 pub async fn peer_measurements_page(
767 &self,
768 after: Option<Did>,
769 limit: NonZeroUsize,
770 ) -> PeerMeasurementPage {
771 self.swarm.peer_measurements_page(after, limit).await
772 }
773
774 pub async fn register_service(&self, name: &str) -> Result<()> {
776 let encoded_did = self
777 .did()
778 .to_string()
779 .encode()
780 .map_err(Error::ServiceRegisterError)?;
781 self.storage_touch_data(name, encoded_did)
782 .await
783 .map_err(|error| match error {
784 Error::EntryError(error) => Error::ServiceRegisterError(error),
785 error => error,
786 })
787 }
788
789 pub async fn get_node_info(&self) -> Result<NodeInfoResponse> {
791 Ok(NodeInfoResponse {
792 version: crate::util::build_version(),
793 swarm: Some(self.swarm.inspect().await.into()),
794 })
795 }
796}
797
798#[cfg_attr(all(feature = "browser", target_family = "wasm"), async_trait::async_trait(?Send))]
799#[cfg_attr(
800 not(all(feature = "browser", target_family = "wasm")),
801 async_trait::async_trait
802)]
803impl OnionDirectoryReader for Processor {
804 fn local_did(&self) -> Did {
805 self.did()
806 }
807
808 fn dht_protocol_mode(&self) -> DhtProtocolMode {
809 self.swarm.dht_protocol_mode()
810 }
811
812 async fn live_online_nodes(&self) -> Result<Vec<OnlineNodeDescriptor>> {
813 self.lookup_online_nodes(false).await
814 }
815
816 async fn live_onion_exits(&self, service: &str) -> Result<Vec<OnionExitDescriptor>> {
817 self.lookup_onion_exits(service, false).await
818 }
819
820 async fn peer_qualities(&self) -> Vec<(Did, PeerQuality)> {
821 self.peer_measurements()
822 .await
823 .into_iter()
824 .map(|measurement| (measurement.did, measurement.quality))
825 .collect()
826 }
827}
828
829#[cfg(all(test, feature = "node"))]
830mod tests;