1use anemo::types::PeerAffinity;
5use anemo::types::PeerInfo;
6use anemo::{Network, Peer, PeerId, Request, Response, types::PeerEvent};
7use fastcrypto::ed25519::{Ed25519PublicKey, Ed25519Signature};
8use futures::StreamExt;
9use mysten_common::debug_fatal;
10use serde::{Deserialize, Serialize};
11use shared_crypto::intent::IntentScope;
12use std::{
13 collections::{BTreeMap, HashMap, HashSet},
14 path::PathBuf,
15 sync::{Arc, RwLock},
16 time::{Duration, Instant},
17};
18
19use crate::endpoint_manager::{AddressSource, EndpointId, EndpointManager};
20use store::{load_stored_peers, save_stored_peers};
21use sui_config::p2p::{AccessType, DiscoveryConfig, P2pConfig};
22use sui_types::crypto::{NetworkKeyPair, NetworkPublicKey, Signer, ToFromBytes, VerifyingKey};
23use sui_types::digests::Digest;
24use sui_types::message_envelope::{Envelope, Message, VerifiedEnvelope};
25use sui_types::multiaddr::Multiaddr;
26use tap::{Pipe, TapFallible};
27use tokio::sync::broadcast::error::RecvError;
28use tokio::sync::mpsc;
29use tokio::{
30 sync::oneshot,
31 task::{AbortHandle, JoinSet},
32};
33use tracing::{debug, info, trace};
34
35const TIMEOUT: Duration = Duration::from_secs(1);
36const ONE_DAY_MILLISECONDS: u64 = 24 * 60 * 60 * 1_000;
37const MAX_ADDRESS_LENGTH: usize = 300;
38const MAX_PEERS_TO_SEND: usize = 200;
39const MAX_ADDRESSES_PER_PEER: usize = 2;
40
41#[allow(clippy::result_large_err)]
42mod generated {
43 include!(concat!(env!("OUT_DIR"), "/sui.Discovery.rs"));
44}
45mod builder;
46mod metrics;
47mod server;
48mod store;
49#[cfg(test)]
50mod tests;
51
52pub use builder::{Builder, UnstartedDiscovery};
53pub use generated::{
54 discovery_client::DiscoveryClient,
55 discovery_server::{Discovery, DiscoveryServer},
56};
57pub use server::{GetKnownPeersRequestV3, GetKnownPeersResponseV2, GetKnownPeersResponseV3};
58
59pub type TrustedPeerP2pAddresses =
62 BTreeMap<PeerId, BTreeMap<AddressSource, Vec<anemo::types::Address>>>;
63
64#[derive(Debug)]
66pub enum DiscoveryMessage {
67 PeerAddressChange {
69 peer_id: PeerId,
70 source: AddressSource,
71 addresses: Vec<anemo::types::Address>,
72 },
73 ReceivedNodeInfo {
75 peer_info: Box<SignedVersionedNodeInfo>,
76 },
77 TrustedPeersUpdated,
80 PeerFailureReport { peer_id: PeerId },
82 GetTrustedPeerP2pAddresses {
85 reply: oneshot::Sender<TrustedPeerP2pAddresses>,
86 },
87}
88
89#[derive(Clone, Debug)]
92pub struct Handle {
93 pub(super) _shutdown_handle: Arc<oneshot::Sender<()>>,
94 pub(super) sender: Sender,
95}
96
97impl Handle {
98 pub fn sender(&self) -> Sender {
99 self.sender.clone()
100 }
101}
102
103#[derive(Clone, Debug)]
106pub struct Sender {
107 pub(super) sender: mpsc::Sender<DiscoveryMessage>,
108}
109
110impl Sender {
111 pub fn peer_address_change(
112 &self,
113 peer_id: PeerId,
114 source: AddressSource,
115 addresses: Vec<anemo::types::Address>,
116 ) {
117 self.sender
118 .try_send(DiscoveryMessage::PeerAddressChange {
119 peer_id,
120 source,
121 addresses,
122 })
123 .expect("Discovery mailbox should not overflow or be closed")
124 }
125
126 pub fn report_peer_failure(&self, peer_id: PeerId) {
127 let _ = self
128 .sender
129 .try_send(DiscoveryMessage::PeerFailureReport { peer_id });
130 }
131
132 pub async fn trusted_peer_p2p_addresses(&self) -> TrustedPeerP2pAddresses {
136 let (s, r) = oneshot::channel();
137 if self
138 .sender
139 .send(DiscoveryMessage::GetTrustedPeerP2pAddresses { reply: s })
140 .await
141 .is_err()
142 {
143 return TrustedPeerP2pAddresses::default();
144 }
145 r.await.unwrap_or_default()
146 }
147}
148
149use self::metrics::Metrics;
150
151struct State {
153 our_info: Option<SignedNodeInfo>,
154 our_info_v2: Option<SignedVersionedNodeInfo>,
155 connected_peers: HashMap<PeerId, ()>,
156 known_peers: HashMap<PeerId, VerifiedSignedNodeInfo>,
157 known_peers_v2: HashMap<PeerId, VerifiedSignedVersionedNodeInfo>,
158 peer_addresses: HashMap<PeerId, BTreeMap<AddressSource, Vec<anemo::types::Address>>>,
159}
160
161#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
166pub struct NodeInfo {
167 pub peer_id: PeerId,
168 pub addresses: Vec<Multiaddr>,
169
170 pub timestamp_ms: u64,
174
175 pub access_type: AccessType,
177}
178
179impl NodeInfo {
180 fn sign(self, keypair: &NetworkKeyPair) -> SignedNodeInfo {
181 let msg = bcs::to_bytes(&self).expect("BCS serialization should not fail");
182 let sig = keypair.sign(&msg);
183 SignedNodeInfo::new_from_data_and_sig(self, sig)
184 }
185}
186
187pub type SignedNodeInfo = Envelope<NodeInfo, Ed25519Signature>;
188
189pub type VerifiedSignedNodeInfo = VerifiedEnvelope<NodeInfo, Ed25519Signature>;
190
191#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
192pub struct NodeInfoDigest(Digest);
193
194impl NodeInfoDigest {
195 pub const fn new(digest: [u8; 32]) -> Self {
196 Self(Digest::new(digest))
197 }
198}
199
200impl Message for NodeInfo {
201 type DigestType = NodeInfoDigest;
202 const SCOPE: IntentScope = IntentScope::DiscoveryPeers;
203
204 fn digest(&self) -> Self::DigestType {
205 unreachable!("NodeInfoDigest is not used today")
206 }
207}
208
209#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
212pub struct NodeInfoV2 {
213 pub addresses: BTreeMap<EndpointId, Vec<Multiaddr>>,
214 pub timestamp_ms: u64,
215 pub access_type: AccessType,
216}
217
218impl NodeInfoV2 {
219 pub fn peer_id(&self) -> Option<PeerId> {
222 self.addresses.keys().find_map(|k| match k {
223 EndpointId::P2p(peer_id) => Some(*peer_id),
224 EndpointId::Consensus(_) => None,
225 })
226 }
227
228 pub fn p2p_addresses(&self) -> &[Multiaddr] {
229 self.addresses
230 .iter()
231 .find_map(|(k, v)| match k {
232 EndpointId::P2p(_) => Some(v.as_slice()),
233 EndpointId::Consensus(_) => None,
234 })
235 .unwrap_or(&[])
236 }
237}
238
239#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
241pub enum VersionedNodeInfo {
242 V1(NodeInfo),
243 V2(NodeInfoV2),
244}
245
246impl VersionedNodeInfo {
247 pub fn peer_id(&self) -> Option<PeerId> {
248 match self {
249 VersionedNodeInfo::V1(info) => Some(info.peer_id),
250 VersionedNodeInfo::V2(info) => info.peer_id(),
251 }
252 }
253
254 pub fn timestamp_ms(&self) -> u64 {
255 match self {
256 VersionedNodeInfo::V1(info) => info.timestamp_ms,
257 VersionedNodeInfo::V2(info) => info.timestamp_ms,
258 }
259 }
260
261 pub fn access_type(&self) -> AccessType {
262 match self {
263 VersionedNodeInfo::V1(info) => info.access_type,
264 VersionedNodeInfo::V2(info) => info.access_type,
265 }
266 }
267
268 pub fn p2p_addresses(&self) -> &[Multiaddr] {
269 match self {
270 VersionedNodeInfo::V1(info) => &info.addresses,
271 VersionedNodeInfo::V2(info) => info.p2p_addresses(),
272 }
273 }
274
275 pub fn sign(self, keypair: &NetworkKeyPair) -> SignedVersionedNodeInfo {
276 let msg = bcs::to_bytes(&self).expect("BCS serialization should not fail");
277 let sig = keypair.sign(&msg);
278 SignedVersionedNodeInfo::new_from_data_and_sig(self, sig)
279 }
280}
281
282#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
283pub struct VersionedNodeInfoDigest(Digest);
284
285impl Message for VersionedNodeInfo {
286 type DigestType = VersionedNodeInfoDigest;
287 const SCOPE: IntentScope = IntentScope::DiscoveryPeers;
288
289 fn digest(&self) -> Self::DigestType {
290 unreachable!("VersionedNodeInfoDigest is not used today")
291 }
292}
293
294pub type SignedVersionedNodeInfo = Envelope<VersionedNodeInfo, Ed25519Signature>;
295pub type VerifiedSignedVersionedNodeInfo = VerifiedEnvelope<VersionedNodeInfo, Ed25519Signature>;
296
297fn verify_versioned_node_info(
307 peer_info: &SignedVersionedNodeInfo,
308) -> Result<VerifiedSignedVersionedNodeInfo, &'static str> {
309 let peer_id = peer_info.peer_id().ok_or("missing P2P peer_id")?;
310
311 let public_key =
312 Ed25519PublicKey::from_bytes(&peer_id.0).map_err(|_| "invalid peer_id public key")?;
313
314 let msg = bcs::to_bytes(peer_info.data()).expect("BCS serialization should not fail");
315 public_key
316 .verify(&msg, peer_info.auth_sig())
317 .map_err(|_| "signature verification failed")?;
318
319 match peer_info.data() {
320 VersionedNodeInfo::V1(info) => {
321 if info.addresses.len() > MAX_ADDRESSES_PER_PEER {
322 return Err("too many addresses");
323 }
324 if !info
325 .addresses
326 .iter()
327 .all(|addr| addr.len() < MAX_ADDRESS_LENGTH && addr.to_anemo_address().is_ok())
328 {
329 return Err("invalid address");
330 }
331 }
332 VersionedNodeInfo::V2(info_v2) => {
333 let mut seen_variants = Vec::new();
335 for endpoint_id in info_v2.addresses.keys() {
336 let variant = std::mem::discriminant(endpoint_id);
337 if seen_variants.contains(&variant) {
338 return Err("duplicate endpoint variant");
339 }
340 seen_variants.push(variant);
341 }
342
343 for (endpoint_id, addrs) in &info_v2.addresses {
344 if addrs.len() > MAX_ADDRESSES_PER_PEER {
345 return Err("too many addresses for endpoint");
346 }
347 if !addrs.iter().all(|addr| addr.len() < MAX_ADDRESS_LENGTH) {
348 return Err("address too long");
349 }
350 if matches!(endpoint_id, EndpointId::P2p(_))
351 && !addrs.iter().all(|addr| addr.to_anemo_address().is_ok())
352 {
353 return Err("invalid P2P address");
354 }
355 }
356
357 let identities_valid = info_v2.addresses.keys().all(|eid| match eid {
358 EndpointId::P2p(_) => true,
359 EndpointId::Consensus(pubkey) => pubkey.as_bytes() == peer_id.0,
360 });
361 if !identities_valid {
362 return Err("non-P2P endpoint identity mismatch");
363 }
364 }
365 }
366
367 Ok(VerifiedSignedVersionedNodeInfo::new_from_verified(
368 peer_info.clone(),
369 ))
370}
371
372struct DiscoveryEventLoop {
373 config: P2pConfig,
374 discovery_config: Arc<DiscoveryConfig>,
375 configured_peers: Arc<HashMap<PeerId, PeerInfo>>,
376 chain_peers: Arc<RwLock<HashSet<PeerId>>>,
377 unidentified_seed_peers: Vec<anemo::types::Address>,
378 network: Network,
379 keypair: NetworkKeyPair,
380 tasks: JoinSet<()>,
381 pending_dials: HashMap<PeerId, AbortHandle>,
382 dial_seed_peers_task: Option<AbortHandle>,
383 shutdown_handle: oneshot::Receiver<()>,
384 state: Arc<RwLock<State>>,
385 mailbox: mpsc::Receiver<DiscoveryMessage>,
386 mailbox_tx: mpsc::Sender<DiscoveryMessage>,
387 metrics: Metrics,
388 consensus_external_address: Option<Multiaddr>,
389 endpoint_manager: EndpointManager,
390 store_path: Option<PathBuf>,
391 peer_cooldowns: HashMap<PeerId, Instant>,
392}
393
394impl DiscoveryEventLoop {
395 pub async fn start(mut self) {
396 info!("Discovery started");
397
398 self.construct_our_info();
399 self.configure_preferred_peers();
400 self.load_stored_peers_on_startup();
401
402 let mut interval = tokio::time::interval(self.discovery_config.interval_period());
403 let mut peer_events = {
404 let (subscriber, _peers) = self.network.subscribe().unwrap();
405 subscriber
406 };
407
408 loop {
409 tokio::select! {
410 now = interval.tick() => {
411 let now_unix = now_unix();
412 self.handle_tick(now.into_std(), now_unix);
413 }
414 peer_event = peer_events.recv() => {
415 self.handle_peer_event(peer_event);
416 },
417 Some(message) = self.mailbox.recv() => {
418 self.handle_message(message);
419 }
420 Some(task_result) = self.tasks.join_next() => {
421 match task_result {
422 Ok(()) => {},
423 Err(e) => {
424 if e.is_cancelled() {
425 } else if e.is_panic() {
427 std::panic::resume_unwind(e.into_panic());
429 } else {
430 panic!("task failed: {e}");
431 }
432 },
433 };
434 },
435 _ = &mut self.shutdown_handle => {
437 break;
438 }
439 }
440 }
441
442 self.save_stored_peers();
443 info!("Discovery ended");
444 }
445
446 fn handle_message(&mut self, message: DiscoveryMessage) {
447 match message {
448 DiscoveryMessage::PeerAddressChange {
449 peer_id,
450 source,
451 addresses,
452 } => {
453 self.handle_peer_address_change(peer_id, source, addresses);
454 }
455 DiscoveryMessage::ReceivedNodeInfo { peer_info } => {
456 let changed = update_known_peers_versioned(
457 self.state.clone(),
458 vec![*peer_info],
459 self.configured_peers.clone(),
460 &self.chain_peers,
461 &self.endpoint_manager,
462 );
463 if changed {
464 self.save_stored_peers();
465 }
466 }
467 DiscoveryMessage::TrustedPeersUpdated => {
468 self.save_stored_peers();
469 }
470 DiscoveryMessage::PeerFailureReport { peer_id } => {
471 self.handle_peer_failure_report(peer_id);
472 }
473 DiscoveryMessage::GetTrustedPeerP2pAddresses { reply } => {
474 let _ = reply.send(self.trusted_peer_p2p_addresses());
475 }
476 }
477 }
478
479 fn trusted_peer_p2p_addresses(&self) -> TrustedPeerP2pAddresses {
480 let state = self.state.read().unwrap();
481 state
482 .peer_addresses
483 .iter()
484 .filter(|(peer_id, _)| {
485 is_trusted_peer(peer_id, &self.configured_peers, &self.chain_peers)
486 })
487 .map(|(peer_id, sources)| (*peer_id, sources.clone()))
488 .collect()
489 }
490
491 fn handle_peer_failure_report(&mut self, peer_id: PeerId) {
492 if self.is_trusted_peer(&peer_id) {
493 info!(?peer_id, "ignoring failure report for trusted peer");
494 return;
495 }
496 let min_peers = self.discovery_config.min_peers_for_disconnect();
497 let connected_count = self.state.read().unwrap().connected_peers.len();
498 if connected_count < min_peers {
499 info!(
500 ?peer_id,
501 connected_count, min_peers, "skipping disconnect, too few connected peers"
502 );
503 return;
504 }
505 info!(
506 ?peer_id,
507 "peer failure reported, disconnecting and adding cooldown"
508 );
509 let _ = self.network.disconnect(peer_id);
510 self.peer_cooldowns.insert(peer_id, Instant::now());
511 }
512
513 fn construct_our_info(&mut self) {
514 if self.state.read().unwrap().our_info.is_some() {
515 return;
516 }
517
518 let peer_id = self.network.peer_id();
519 let timestamp_ms = now_unix();
520 let access_type = self.discovery_config.access_type();
521
522 let addresses: Vec<Multiaddr> = self
523 .config
524 .external_address
525 .clone()
526 .and_then(|addr| addr.to_anemo_address().ok().map(|_| addr))
527 .into_iter()
528 .collect();
529
530 let our_info = NodeInfo {
531 peer_id,
532 addresses: addresses.clone(),
533 timestamp_ms,
534 access_type,
535 }
536 .sign(&self.keypair);
537
538 let mut addresses_map = BTreeMap::new();
539 addresses_map.insert(EndpointId::P2p(peer_id), addresses);
540 if let Some(consensus_addr) = &self.consensus_external_address {
541 let network_pubkey =
545 NetworkPublicKey::from_bytes(&peer_id.0).expect("PeerId is a valid public key");
546 addresses_map.insert(
547 EndpointId::Consensus(network_pubkey),
548 vec![consensus_addr.clone()],
549 );
550 }
551 let our_info_v2 = VersionedNodeInfo::V2(NodeInfoV2 {
552 addresses: addresses_map,
553 timestamp_ms,
554 access_type,
555 })
556 .sign(&self.keypair);
557
558 let mut state = self.state.write().unwrap();
559 state.our_info = Some(our_info);
560 state.our_info_v2 = Some(our_info_v2);
561 }
562
563 fn configure_preferred_peers(&mut self) {
564 let peers: Vec<_> = self.configured_peers.values().cloned().collect();
565 for peer_info in peers {
566 debug!(?peer_info, "Add configured preferred peer");
567 match peer_info.affinity {
568 PeerAffinity::High => {
569 self.handle_peer_address_change(
570 peer_info.peer_id,
571 AddressSource::Seed,
572 peer_info.address,
573 );
574 }
575 _ => {
576 self.network.known_peers().insert(peer_info);
579 }
580 }
581 }
582 }
583
584 fn update_our_info_timestamp(&mut self, now_unix: u64) {
585 let state = &mut self.state.write().unwrap();
586
587 if let Some(our_info) = &state.our_info {
588 let mut data = our_info.data().clone();
589 data.timestamp_ms = now_unix;
590 state.our_info = Some(data.sign(&self.keypair));
591 }
592
593 if let Some(our_info_v2) = &state.our_info_v2 {
594 let mut data = our_info_v2.data().clone();
595 match &mut data {
596 VersionedNodeInfo::V1(info) => info.timestamp_ms = now_unix,
597 VersionedNodeInfo::V2(info) => info.timestamp_ms = now_unix,
598 }
599 state.our_info_v2 = Some(data.sign(&self.keypair));
600 }
601 }
602
603 fn handle_peer_address_change(
604 &mut self,
605 peer_id: PeerId,
606 source: AddressSource,
607 addresses: Vec<anemo::types::Address>,
608 ) {
609 debug!(
610 ?peer_id,
611 ?source,
612 ?addresses,
613 "Received peer address change"
614 );
615
616 if source == AddressSource::Chain {
618 if addresses.is_empty() {
619 self.chain_peers.write().unwrap().remove(&peer_id);
620 } else {
621 self.chain_peers.write().unwrap().insert(peer_id);
622 }
623 }
624
625 {
627 let mut state = self.state.write().unwrap();
628 let source_map = state.peer_addresses.entry(peer_id).or_default();
629
630 if addresses.is_empty() {
631 source_map.remove(&source);
632 if source_map.is_empty() {
633 state.peer_addresses.remove(&peer_id);
634 }
635 } else {
636 source_map.insert(source, addresses);
637 }
638
639 if source == AddressSource::Chain
645 && !state
646 .peer_addresses
647 .get(&peer_id)
648 .is_some_and(|s| s.contains_key(&AddressSource::Discovery))
649 && let Some(addrs) = state
650 .known_peers_v2
651 .get(&peer_id)
652 .and_then(|info| match info.data() {
653 VersionedNodeInfo::V2(v2) => Some(v2.p2p_addresses()),
654 _ => None,
655 })
656 {
657 let anemo_addrs: Vec<_> = addrs
658 .iter()
659 .filter_map(|a| a.to_anemo_address().ok())
660 .collect();
661 if !anemo_addrs.is_empty() {
662 state
663 .peer_addresses
664 .entry(peer_id)
665 .or_default()
666 .insert(AddressSource::Discovery, anemo_addrs);
667 }
668 }
669 }
670
671 self.reconfigure_peer_addresses(peer_id);
673 }
674
675 fn reconfigure_peer_addresses(&mut self, peer_id: PeerId) {
678 let priority = self
679 .state
680 .read()
681 .unwrap()
682 .peer_addresses
683 .get(&peer_id)
684 .and_then(|sources| {
685 sources
686 .first_key_value()
687 .map(|(source, addrs)| (*source, addrs.clone()))
688 });
689
690 if self.is_trusted_peer(&peer_id) {
694 self.metrics.set_active_p2p_address_source(
695 &peer_id.to_string(),
696 priority.as_ref().map(|(source, _)| *source),
697 );
698 }
699
700 let priority_addresses = priority.map(|(_, addrs)| addrs).unwrap_or_default();
701 let current_addresses = self
702 .network
703 .known_peers()
704 .get(&peer_id)
705 .map(|info| info.address.clone())
706 .unwrap_or_default();
707 if priority_addresses != current_addresses {
708 let new_peer_info = PeerInfo {
709 peer_id,
710 affinity: PeerAffinity::High,
711 address: priority_addresses.clone(),
712 };
713
714 self.network.known_peers().insert(new_peer_info);
715
716 if !current_addresses.is_empty() {
718 let _ = self.network.disconnect(peer_id);
719
720 if let Some(address) = priority_addresses.first().cloned() {
721 let network = self.network.clone();
722 self.tasks.spawn(async move {
723 let _ = network.connect_with_peer_id(address, peer_id).await;
725 });
726 }
727 }
728 }
729 }
730
731 fn load_stored_peers_on_startup(&mut self) {
732 let Some(path) = &self.store_path else {
733 return;
734 };
735 let entries = load_stored_peers(path);
736 if entries.is_empty() {
737 return;
738 }
739 info!(
740 count = entries.len(),
741 "Loaded stored peer addresses from {}",
742 path.display()
743 );
744
745 update_known_peers_versioned(
746 self.state.clone(),
747 entries,
748 self.configured_peers.clone(),
749 &self.chain_peers,
750 &self.endpoint_manager,
751 );
752
753 while let Ok(msg) = self.mailbox.try_recv() {
756 self.handle_message(msg);
757 }
758 }
759
760 fn save_stored_peers(&self) {
761 let Some(path) = &self.store_path else {
762 return;
763 };
764 let state = self.state.read().unwrap();
765 let peers_to_save: Vec<SignedVersionedNodeInfo> = state
766 .known_peers_v2
767 .iter()
768 .filter(|(pid, _)| self.is_trusted_peer(pid))
769 .map(|(_, verified)| verified.inner().clone())
770 .collect();
771 drop(state);
772 save_stored_peers(path, &peers_to_save);
773 }
774
775 fn handle_peer_event(&mut self, peer_event: Result<PeerEvent, RecvError>) {
776 match peer_event {
777 Ok(PeerEvent::NewPeer(peer_id)) => {
778 if let Some(peer) = self.network.peer(peer_id) {
779 self.state
780 .write()
781 .unwrap()
782 .connected_peers
783 .insert(peer_id, ());
784
785 self.tasks.spawn(query_peer_for_their_known_peers(
787 peer,
788 self.discovery_config.clone(),
789 self.state.clone(),
790 self.configured_peers.clone(),
791 self.chain_peers.clone(),
792 self.endpoint_manager.clone(),
793 self.mailbox_sender(),
794 ));
795 }
796 }
797 Ok(PeerEvent::LostPeer(peer_id, _)) => {
798 self.state.write().unwrap().connected_peers.remove(&peer_id);
799 }
800
801 Err(RecvError::Closed) => {
802 panic!("PeerEvent channel shouldn't be able to be closed");
803 }
804
805 Err(RecvError::Lagged(_)) => {
806 trace!("State-Sync fell behind processing PeerEvents");
807 }
808 }
809 }
810
811 fn handle_tick(&mut self, _now: std::time::Instant, now_unix: u64) {
812 self.update_our_info_timestamp(now_unix);
813
814 self.tasks
815 .spawn(query_connected_peers_for_their_known_peers(
816 self.network.clone(),
817 self.discovery_config.clone(),
818 self.state.clone(),
819 self.configured_peers.clone(),
820 self.chain_peers.clone(),
821 self.endpoint_manager.clone(),
822 self.mailbox_sender(),
823 ));
824
825 let mut culled_trusted_peers = Vec::new();
827 {
828 let mut state = self.state.write().unwrap();
829 state
830 .known_peers
831 .retain(|_k, v| now_unix.saturating_sub(v.timestamp_ms) < ONE_DAY_MILLISECONDS);
832 state.known_peers_v2.retain(|k, v| {
833 let keep = now_unix.saturating_sub(v.timestamp_ms()) < ONE_DAY_MILLISECONDS;
834 if !keep && self.is_trusted_peer(k) {
835 culled_trusted_peers.push(*k);
836 }
837 keep
838 });
839 }
840
841 for peer_id in culled_trusted_peers {
844 self.endpoint_manager
845 .clear_source(peer_id, AddressSource::Discovery);
846 }
847
848 self.pending_dials.retain(|_k, v| !v.is_finished());
850 if let Some(abort_handle) = &self.dial_seed_peers_task
851 && abort_handle.is_finished()
852 {
853 self.dial_seed_peers_task = None;
854 }
855
856 let cooldown = self.discovery_config.peer_failure_cooldown();
857 self.peer_cooldowns
858 .retain(|_, since| since.elapsed() < cooldown);
859
860 let state = self.state.read().unwrap();
862 let our_peer_id = self.network.peer_id();
863
864 let mut peers_with_external_address: HashSet<PeerId> = HashSet::new();
869 for (peer_id, info) in state.known_peers.iter() {
870 if !info.addresses.is_empty() {
871 peers_with_external_address.insert(*peer_id);
872 }
873 }
874 for (peer_id, info) in state.known_peers_v2.iter() {
875 if !info.p2p_addresses().is_empty() {
876 peers_with_external_address.insert(*peer_id);
877 }
878 }
879 self.metrics
880 .set_num_peers_with_external_address(peers_with_external_address.len() as i64);
881
882 let mut preferred: HashMap<PeerId, NodeInfo> = HashMap::new();
886 let mut cooldown_peers: HashMap<PeerId, NodeInfo> = HashMap::new();
887
888 for (peer_id, info) in state.known_peers.iter() {
889 if *peer_id != our_peer_id
890 && !info.addresses.is_empty()
891 && !state.connected_peers.contains_key(peer_id)
892 && !self.pending_dials.contains_key(peer_id)
893 && !state.peer_addresses.contains_key(peer_id)
894 {
895 if self.peer_cooldowns.contains_key(peer_id) {
896 cooldown_peers.insert(*peer_id, info.data().clone());
897 } else {
898 preferred.insert(*peer_id, info.data().clone());
899 }
900 }
901 }
902 for (peer_id, info) in state.known_peers_v2.iter() {
903 let p2p_addresses = info.p2p_addresses();
904 if *peer_id != our_peer_id
905 && !p2p_addresses.is_empty()
906 && !state.connected_peers.contains_key(peer_id)
907 && !self.pending_dials.contains_key(peer_id)
908 && !state.peer_addresses.contains_key(peer_id)
909 {
910 let node_info = NodeInfo {
911 peer_id: *peer_id,
912 addresses: p2p_addresses.to_vec(),
913 timestamp_ms: info.timestamp_ms(),
914 access_type: info.access_type(),
915 };
916 if self.peer_cooldowns.contains_key(peer_id) {
917 if cooldown_peers
918 .get(peer_id)
919 .is_none_or(|existing| info.timestamp_ms() > existing.timestamp_ms)
920 {
921 cooldown_peers.insert(*peer_id, node_info);
922 }
923 } else if preferred
924 .get(peer_id)
925 .is_none_or(|existing| info.timestamp_ms() > existing.timestamp_ms)
926 {
927 preferred.insert(*peer_id, node_info);
928 }
929 }
930 }
931
932 let number_of_connections = state.connected_peers.len();
933 let number_to_dial = self
934 .discovery_config
935 .target_concurrent_connections()
936 .saturating_sub(number_of_connections);
937
938 use rand::seq::IteratorRandom;
939 let mut rng = rand::thread_rng();
940
941 let mut to_dial: Vec<_> = preferred
942 .into_iter()
943 .choose_multiple(&mut rng, number_to_dial);
944
945 let remaining = number_to_dial.saturating_sub(to_dial.len());
946 to_dial.extend(
947 cooldown_peers
948 .into_iter()
949 .choose_multiple(&mut rng, remaining),
950 );
951
952 for (peer_id, info) in to_dial {
953 let abort_handle = self
954 .tasks
955 .spawn(try_to_connect_to_peer(self.network.clone(), info));
956 self.pending_dials.insert(peer_id, abort_handle);
957 }
958
959 let has_peers_to_dial = || {
962 self.configured_peers
963 .values()
964 .any(|p| p.affinity == PeerAffinity::High)
965 || !self.unidentified_seed_peers.is_empty()
966 };
967 if self.dial_seed_peers_task.is_none()
968 && state.connected_peers.is_empty()
969 && self.pending_dials.is_empty()
970 && has_peers_to_dial()
971 {
972 let abort_handle = self.tasks.spawn(try_to_connect_to_seed_peers(
973 self.network.clone(),
974 self.discovery_config.clone(),
975 self.configured_peers.clone(),
976 self.unidentified_seed_peers.clone(),
977 ));
978
979 self.dial_seed_peers_task = Some(abort_handle);
980 }
981 }
982
983 fn is_trusted_peer(&self, peer_id: &PeerId) -> bool {
984 is_trusted_peer(peer_id, &self.configured_peers, &self.chain_peers)
985 }
986
987 fn mailbox_sender(&self) -> mpsc::Sender<DiscoveryMessage> {
988 self.mailbox_tx.clone()
989 }
990}
991
992async fn try_to_connect_to_peer(network: Network, info: NodeInfo) {
993 debug!("Connecting to peer {info:?}");
994 for multiaddr in &info.addresses {
995 if let Ok(address) = multiaddr.to_anemo_address() {
996 if network
998 .connect_with_peer_id(address, info.peer_id)
999 .await
1000 .tap_err(|e| {
1001 debug!(
1002 "error dialing {} at address '{}': {e}",
1003 info.peer_id.short_display(4),
1004 multiaddr
1005 )
1006 })
1007 .is_ok()
1008 {
1009 return;
1010 }
1011 }
1012 }
1013}
1014
1015async fn try_to_connect_to_seed_peers(
1016 network: Network,
1017 config: Arc<DiscoveryConfig>,
1018 configured_peers: Arc<HashMap<PeerId, PeerInfo>>,
1019 unidentified_seed_peers: Vec<anemo::types::Address>,
1020) {
1021 let high_affinity_peers: Vec<_> = configured_peers
1022 .values()
1023 .filter(|p| p.affinity == PeerAffinity::High)
1024 .cloned()
1025 .collect();
1026 debug!(
1027 ?high_affinity_peers,
1028 ?unidentified_seed_peers,
1029 "Connecting to seed peers"
1030 );
1031 let network = &network;
1032
1033 let with_peer_id = high_affinity_peers.into_iter().flat_map(|peer_info| {
1035 peer_info
1036 .address
1037 .into_iter()
1038 .map(move |addr| (Some(peer_info.peer_id), addr))
1039 });
1040 let without_peer_id = unidentified_seed_peers.into_iter().map(|addr| (None, addr));
1041 futures::stream::iter(with_peer_id.chain(without_peer_id))
1042 .for_each_concurrent(
1043 config.target_concurrent_connections(),
1044 |(peer_id, address)| async move {
1045 let _ = if let Some(peer_id) = peer_id {
1047 network
1048 .connect_with_peer_id(address.clone(), peer_id)
1049 .await
1050 .tap_err(|e| {
1051 debug!(
1052 "error dialing peer {} at '{}': {e}",
1053 peer_id.short_display(4),
1054 address
1055 )
1056 })
1057 } else {
1058 network
1059 .connect(address.clone())
1060 .await
1061 .tap_err(|e| debug!("error dialing address '{}': {e}", address))
1062 };
1063 },
1064 )
1065 .await;
1066}
1067
1068async fn query_peer_for_known_peers_v2(peer: Peer) -> Option<Vec<SignedNodeInfo>> {
1069 let mut client = DiscoveryClient::new(peer);
1070 let request = Request::new(()).with_timeout(TIMEOUT);
1071 client
1072 .get_known_peers_v2(request)
1073 .await
1074 .ok()
1075 .map(Response::into_inner)
1076 .map(
1077 |GetKnownPeersResponseV2 {
1078 own_info,
1079 mut known_peers,
1080 }| {
1081 if !own_info.addresses.is_empty() {
1082 known_peers.push(own_info)
1083 }
1084 known_peers
1085 },
1086 )
1087}
1088
1089async fn query_peer_for_their_known_peers(
1090 peer: Peer,
1091 discovery_config: Arc<DiscoveryConfig>,
1092 state: Arc<RwLock<State>>,
1093 configured_peers: Arc<HashMap<PeerId, PeerInfo>>,
1094 chain_peers: Arc<RwLock<HashSet<PeerId>>>,
1095 endpoint_manager: EndpointManager,
1096 mailbox_tx: mpsc::Sender<DiscoveryMessage>,
1097) {
1098 if discovery_config.use_get_known_peers_v3() {
1100 let our_info_v2 = state.read().unwrap().our_info_v2.clone();
1101 if let Some(own_info) = our_info_v2 {
1102 let peer_for_v3 = peer.clone();
1103 let v3_query = async move {
1104 let mut client = DiscoveryClient::new(peer_for_v3);
1105 let request =
1106 Request::new(GetKnownPeersRequestV3 { own_info }).with_timeout(TIMEOUT);
1107 client
1108 .get_known_peers_v3(request)
1109 .await
1110 .ok()
1111 .map(Response::into_inner)
1112 .map(
1113 |GetKnownPeersResponseV3 {
1114 own_info,
1115 mut known_peers,
1116 }| {
1117 if !own_info.p2p_addresses().is_empty() {
1118 known_peers.push(own_info)
1119 }
1120 known_peers
1121 },
1122 )
1123 };
1124
1125 let (found_peers_v2, found_peers_v3) =
1126 tokio::join!(query_peer_for_known_peers_v2(peer), v3_query);
1127
1128 if let Some(found_peers) = found_peers_v2 {
1129 update_known_peers(
1130 state.clone(),
1131 found_peers,
1132 configured_peers.clone(),
1133 &chain_peers,
1134 );
1135 }
1136 if let Some(found_peers) = found_peers_v3 {
1137 let changed = update_known_peers_versioned(
1138 state,
1139 found_peers,
1140 configured_peers,
1141 &chain_peers,
1142 &endpoint_manager,
1143 );
1144 if changed {
1145 let _ = mailbox_tx.try_send(DiscoveryMessage::TrustedPeersUpdated);
1146 }
1147 }
1148 return;
1149 }
1150 }
1151
1152 if let Some(found_peers) = query_peer_for_known_peers_v2(peer).await {
1154 update_known_peers(state, found_peers, configured_peers, &chain_peers);
1155 }
1156}
1157
1158async fn query_connected_peers_for_their_known_peers(
1159 network: Network,
1160 config: Arc<DiscoveryConfig>,
1161 state: Arc<RwLock<State>>,
1162 configured_peers: Arc<HashMap<PeerId, PeerInfo>>,
1163 chain_peers: Arc<RwLock<HashSet<PeerId>>>,
1164 endpoint_manager: EndpointManager,
1165 mailbox_tx: mpsc::Sender<DiscoveryMessage>,
1166) {
1167 use rand::seq::IteratorRandom;
1168
1169 let peers_to_query: Vec<_> = network
1170 .peers()
1171 .into_iter()
1172 .flat_map(|id| network.peer(id))
1173 .choose_multiple(&mut rand::thread_rng(), config.peers_to_query());
1174
1175 let v2_query = {
1177 let peers = peers_to_query.clone();
1178 let peers_to_query_count = config.peers_to_query();
1179 async move {
1180 peers
1181 .into_iter()
1182 .map(DiscoveryClient::new)
1183 .map(|mut client| async move {
1184 let request = Request::new(()).with_timeout(TIMEOUT);
1185 client
1186 .get_known_peers_v2(request)
1187 .await
1188 .ok()
1189 .map(Response::into_inner)
1190 .map(
1191 |GetKnownPeersResponseV2 {
1192 own_info,
1193 mut known_peers,
1194 }| {
1195 if !own_info.addresses.is_empty() {
1196 known_peers.push(own_info)
1197 }
1198 known_peers
1199 },
1200 )
1201 })
1202 .pipe(futures::stream::iter)
1203 .buffer_unordered(peers_to_query_count)
1204 .filter_map(std::future::ready)
1205 .flat_map(futures::stream::iter)
1206 .collect::<Vec<_>>()
1207 .await
1208 }
1209 };
1210
1211 if config.use_get_known_peers_v3() {
1213 let our_info_v2 = state.read().unwrap().our_info_v2.clone();
1214 if let Some(own_info) = our_info_v2 {
1215 let v3_query = {
1216 let peers_to_query_count = config.peers_to_query();
1217 async move {
1218 peers_to_query
1219 .into_iter()
1220 .map(DiscoveryClient::new)
1221 .map(|mut client| {
1222 let own_info = own_info.clone();
1223 async move {
1224 let request = Request::new(GetKnownPeersRequestV3 { own_info })
1225 .with_timeout(TIMEOUT);
1226 client
1227 .get_known_peers_v3(request)
1228 .await
1229 .ok()
1230 .map(Response::into_inner)
1231 .map(
1232 |GetKnownPeersResponseV3 {
1233 own_info,
1234 mut known_peers,
1235 }| {
1236 if !own_info.p2p_addresses().is_empty() {
1237 known_peers.push(own_info)
1238 }
1239 known_peers
1240 },
1241 )
1242 }
1243 })
1244 .pipe(futures::stream::iter)
1245 .buffer_unordered(peers_to_query_count)
1246 .filter_map(std::future::ready)
1247 .flat_map(futures::stream::iter)
1248 .collect::<Vec<_>>()
1249 .await
1250 }
1251 };
1252
1253 let (found_peers_v2, found_peers_v3) = tokio::join!(v2_query, v3_query);
1254
1255 update_known_peers(
1256 state.clone(),
1257 found_peers_v2,
1258 configured_peers.clone(),
1259 &chain_peers,
1260 );
1261 let changed = update_known_peers_versioned(
1262 state,
1263 found_peers_v3,
1264 configured_peers,
1265 &chain_peers,
1266 &endpoint_manager,
1267 );
1268 if changed {
1269 let _ = mailbox_tx.try_send(DiscoveryMessage::TrustedPeersUpdated);
1270 }
1271 return;
1272 }
1273 }
1274
1275 let found_peers_v2 = v2_query.await;
1277 update_known_peers(state, found_peers_v2, configured_peers, &chain_peers);
1278}
1279
1280fn update_known_peers(
1281 state: Arc<RwLock<State>>,
1282 found_peers: Vec<SignedNodeInfo>,
1283 configured_peers: Arc<HashMap<PeerId, PeerInfo>>,
1284 chain_peers: &Arc<RwLock<HashSet<PeerId>>>,
1285) {
1286 use std::collections::hash_map::Entry;
1287
1288 let now_unix = now_unix();
1289 let our_peer_id = state.read().unwrap().our_info.clone().unwrap().peer_id;
1290 let known_peers = &mut state.write().unwrap().known_peers;
1291 for peer_info in found_peers.into_iter().take(MAX_PEERS_TO_SEND + 1) {
1293 if peer_info.timestamp_ms > now_unix.saturating_add(30 * 1_000) || now_unix.saturating_sub(peer_info.timestamp_ms) > ONE_DAY_MILLISECONDS
1298 {
1299 continue;
1300 }
1301
1302 if peer_info.peer_id == our_peer_id {
1303 continue;
1304 }
1305
1306 let is_restricted = match peer_info.access_type {
1307 AccessType::Public => false,
1308 AccessType::Private | AccessType::Trusted => true,
1309 };
1310 if is_restricted && !is_trusted_peer(&peer_info.peer_id, &configured_peers, chain_peers) {
1311 continue;
1312 }
1313
1314 if peer_info.addresses.len() > MAX_ADDRESSES_PER_PEER {
1316 continue;
1317 }
1318
1319 if !peer_info
1321 .addresses
1322 .iter()
1323 .all(|addr| addr.len() < MAX_ADDRESS_LENGTH && addr.to_anemo_address().is_ok())
1324 {
1325 continue;
1326 }
1327 let Ok(public_key) = Ed25519PublicKey::from_bytes(&peer_info.peer_id.0) else {
1328 debug_fatal!(
1329 "Failed to convert anemo PeerId {:?} to Ed25519PublicKey",
1331 peer_info.peer_id
1332 );
1333 continue;
1334 };
1335 let msg = bcs::to_bytes(peer_info.data()).expect("BCS serialization should not fail");
1336 if let Err(e) = public_key.verify(&msg, peer_info.auth_sig()) {
1337 info!(
1338 "Discovery failed to verify signature for NodeInfo for peer {:?}: {e:?}",
1339 peer_info.peer_id
1340 );
1341 continue;
1343 }
1344 let peer = VerifiedSignedNodeInfo::new_from_verified(peer_info);
1345
1346 match known_peers.entry(peer.peer_id) {
1347 Entry::Occupied(mut o) => {
1348 if peer.timestamp_ms > o.get().timestamp_ms {
1349 o.insert(peer);
1350 }
1351 }
1352 Entry::Vacant(v) => {
1353 v.insert(peer);
1354 }
1355 }
1356 }
1357}
1358
1359fn update_known_peers_versioned(
1361 state: Arc<RwLock<State>>,
1362 found_peers: Vec<SignedVersionedNodeInfo>,
1363 configured_peers: Arc<HashMap<PeerId, PeerInfo>>,
1364 chain_peers: &Arc<RwLock<HashSet<PeerId>>>,
1365 endpoint_manager: &EndpointManager,
1366) -> bool {
1367 use std::collections::hash_map::Entry;
1368
1369 let now_unix = now_unix();
1370 let our_peer_id = state
1371 .read()
1372 .unwrap()
1373 .our_info_v2
1374 .as_ref()
1375 .and_then(|info| info.peer_id());
1376
1377 let mut verified_peers = Vec::new();
1380 let mut endpoint_updates = Vec::new();
1381
1382 for peer_info in found_peers.into_iter().take(MAX_PEERS_TO_SEND + 1) {
1383 let timestamp_ms = peer_info.timestamp_ms();
1384
1385 if timestamp_ms > now_unix.saturating_add(30 * 1_000)
1386 || now_unix.saturating_sub(timestamp_ms) > ONE_DAY_MILLISECONDS
1387 {
1388 continue;
1389 }
1390
1391 let Some(peer_id) = peer_info.peer_id() else {
1392 continue;
1393 };
1394
1395 if Some(peer_id) == our_peer_id {
1396 continue;
1397 }
1398
1399 let is_restricted = match peer_info.access_type() {
1400 AccessType::Public => false,
1401 AccessType::Private | AccessType::Trusted => true,
1402 };
1403 let is_trusted = is_trusted_peer(&peer_id, &configured_peers, chain_peers);
1404 if is_restricted && !is_trusted {
1405 continue;
1406 }
1407
1408 let peer = match verify_versioned_node_info(&peer_info) {
1409 Ok(verified) => verified,
1410 Err(reason) => {
1411 info!("Discovery rejecting VersionedNodeInfo for peer {peer_id:?}: {reason}");
1412 continue;
1413 }
1414 };
1415
1416 if is_trusted && let VersionedNodeInfo::V2(info_v2) = peer_info.data() {
1419 for (endpoint_id, addrs) in &info_v2.addresses {
1420 if !addrs.is_empty() {
1421 endpoint_updates.push((endpoint_id.clone(), addrs.clone()));
1422 }
1423 }
1424 }
1425
1426 verified_peers.push((peer_id, peer, is_trusted));
1427 }
1428
1429 let mut trusted_peer_changed = false;
1432 {
1433 let known_peers_v2 = &mut state.write().unwrap().known_peers_v2;
1434 for (peer_id, peer, is_trusted) in verified_peers {
1435 match known_peers_v2.entry(peer_id) {
1436 Entry::Occupied(mut o) => {
1437 if peer.timestamp_ms() > o.get().timestamp_ms() {
1438 o.insert(peer);
1439 if is_trusted {
1440 trusted_peer_changed = true;
1441 }
1442 }
1443 }
1444 Entry::Vacant(v) => {
1445 v.insert(peer);
1446 if is_trusted {
1447 trusted_peer_changed = true;
1448 }
1449 }
1450 }
1451 }
1452 }
1453
1454 for (endpoint_id, addrs) in endpoint_updates {
1459 let _ = endpoint_manager.update_endpoint(endpoint_id, AddressSource::Discovery, addrs);
1460 }
1461
1462 trusted_peer_changed
1463}
1464
1465pub(super) fn is_trusted_peer(
1468 peer_id: &PeerId,
1469 configured_peers: &HashMap<PeerId, PeerInfo>,
1470 chain_peers: &Arc<RwLock<HashSet<PeerId>>>,
1471) -> bool {
1472 configured_peers.contains_key(peer_id) || chain_peers.read().unwrap().contains(peer_id)
1473}
1474
1475pub(super) fn now_unix() -> u64 {
1476 use std::time::{SystemTime, UNIX_EPOCH};
1477
1478 SystemTime::now()
1479 .duration_since(UNIX_EPOCH)
1480 .unwrap()
1481 .as_millis() as u64
1482}