1use crate::{
13 backend,
14 metrics::METRICS,
15 rlpx::{connection::server::PeerConnection, p2p::Capability},
16 types::{Node, NodeRecord},
17 utils::distance,
18};
19use bytes::Bytes;
20use ethrex_common::{H256, U256};
21use ethrex_storage::Store;
22use indexmap::IndexMap;
23use rand::distributions::WeightedIndex;
24use rand::prelude::Distribution;
25use rand::seq::{IteratorRandom, SliceRandom};
26use rustc_hash::{FxHashMap, FxHashSet};
27use spawned_concurrency::{
28 actor,
29 error::ActorError,
30 protocol,
31 tasks::{Actor, ActorRef, ActorStart as _, Context, Handler, Response, send_message_on},
32};
33use std::{
34 net::IpAddr,
35 time::{Duration, Instant},
36};
37
38const MAX_SCORE: i64 = 50;
39const MIN_SCORE: i64 = -50;
40const MIN_SCORE_CRITICAL: i64 = MIN_SCORE * 3;
42const SCORE_WEIGHT: i64 = 1;
44const REQUESTS_WEIGHT: i64 = 1;
46const MAX_CONCURRENT_REQUESTS_PER_PEER: i64 = 100;
48pub const TARGET_PEERS: usize = 100;
50pub(crate) const MAX_NODES_IN_NEIGHBORS_PACKET: usize = 16;
52const MAX_ENRS_PER_FINDNODE_RESPONSE: usize = 16;
54
55const NUMBER_OF_BUCKETS: usize = 256;
57pub const MAX_NODES_PER_BUCKET: usize = 16;
59const MAX_REPLACEMENTS_PER_BUCKET: usize = 10;
61const MAX_CONNECTION_POOL_SIZE: usize = 10_000;
67
68#[derive(Debug, Clone, Default)]
71pub struct KBucket {
72 pub(crate) contacts: Vec<(H256, Contact)>,
73 pub(crate) replacements: Vec<(H256, Contact)>,
74}
75
76impl KBucket {
77 fn get(&self, node_id: &H256) -> Option<&Contact> {
79 self.contacts
80 .iter()
81 .find(|(id, _)| id == node_id)
82 .map(|(_, c)| c)
83 }
84
85 fn get_any(&self, node_id: &H256) -> Option<&Contact> {
87 self.get(node_id).or_else(|| {
88 self.replacements
89 .iter()
90 .find(|(id, _)| id == node_id)
91 .map(|(_, c)| c)
92 })
93 }
94
95 fn get_mut(&mut self, node_id: &H256) -> Option<&mut Contact> {
97 if let Some((_, c)) = self.contacts.iter_mut().find(|(id, _)| id == node_id) {
98 return Some(c);
99 }
100 self.replacements
101 .iter_mut()
102 .find(|(id, _)| id == node_id)
103 .map(|(_, c)| c)
104 }
105
106 fn contains(&self, node_id: &H256) -> bool {
108 self.contacts.iter().any(|(id, _)| id == node_id)
109 || self.replacements.iter().any(|(id, _)| id == node_id)
110 }
111
112 fn insert(&mut self, node_id: H256, contact: Contact) -> bool {
115 if self.contacts.len() < MAX_NODES_PER_BUCKET {
116 self.contacts.push((node_id, contact));
117 true
118 } else {
119 self.insert_replacement(node_id, contact);
120 false
121 }
122 }
123
124 fn insert_replacement(&mut self, node_id: H256, contact: Contact) {
126 if self.replacements.len() >= MAX_REPLACEMENTS_PER_BUCKET {
127 self.replacements.remove(0);
128 }
129 self.replacements.push((node_id, contact));
130 }
131
132 fn remove_and_promote(&mut self, node_id: &H256) -> Option<H256> {
135 let idx = self.contacts.iter().position(|(id, _)| id == node_id)?;
136 self.contacts.remove(idx);
137 if !self.replacements.is_empty() {
138 let (replacement_id, replacement) = self.replacements.remove(0);
139 self.contacts.push((replacement_id, replacement));
140 Some(replacement_id)
141 } else {
142 None
143 }
144 }
145}
146
147fn bucket_index(local_node_id: &H256, node_id: &H256) -> Option<usize> {
152 let xor = *local_node_id ^ *node_id;
153 let dist = U256::from_big_endian(xor.as_bytes());
154 if dist.is_zero() {
155 None
156 } else {
157 Some(dist.bits() - 1)
158 }
159}
160
161pub(crate) fn xor_distance(a: &H256, b: &H256) -> H256 {
165 *a ^ *b
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
171pub enum DiscoveryProtocol {
172 Discv4,
174 Discv5,
176}
177
178pub use crate::discv5::session::Session;
181
182#[derive(Debug, Clone)]
183pub struct Contact {
184 pub node: Node,
185 pub is_discv4: bool,
187 pub is_discv5: bool,
189 pub validation_timestamp: Option<Instant>,
192 pub ping_id: Option<Bytes>,
197
198 pub enr_request_hash: Option<H256>,
201
202 pub record: Option<NodeRecord>,
204 pub disposable: bool,
206 pub knows_us: bool,
208 pub unwanted: bool,
210 pub is_fork_id_valid: Option<bool>,
212 session: Option<Session>,
214}
215
216impl Contact {
217 pub fn was_validated(&self) -> bool {
218 self.validation_timestamp.is_some() && !self.has_pending_ping()
219 }
220
221 pub fn has_pending_ping(&self) -> bool {
222 self.ping_id.is_some()
223 }
224
225 pub fn record_ping_sent(&mut self, ping_id: Bytes) {
226 self.validation_timestamp = Some(Instant::now());
227 self.ping_id = Some(ping_id);
228 }
229
230 pub fn record_enr_request_sent(&mut self, request_hash: H256) {
231 self.enr_request_hash = Some(request_hash);
232 }
233
234 pub fn record_enr_response_received(&mut self, request_hash: H256, record: NodeRecord) {
236 if self
237 .enr_request_hash
238 .take_if(|h| *h == request_hash)
239 .is_some()
240 {
241 self.record = Some(record);
242 }
243 }
244
245 pub fn has_pending_enr_request(&self) -> bool {
246 self.enr_request_hash.is_some()
247 }
248}
249
250impl Contact {
251 pub fn new(node: Node, protocol: DiscoveryProtocol) -> Self {
252 Self {
253 node,
254 is_discv4: protocol == DiscoveryProtocol::Discv4,
255 is_discv5: protocol == DiscoveryProtocol::Discv5,
256 validation_timestamp: None,
257 ping_id: None,
258 enr_request_hash: None,
259 record: None,
260 disposable: false,
261 knows_us: true,
262 unwanted: false,
263 is_fork_id_valid: None,
264 session: None,
265 }
266 }
267
268 pub fn supports_protocol(&self, protocol: DiscoveryProtocol) -> bool {
270 match protocol {
271 DiscoveryProtocol::Discv4 => self.is_discv4,
272 DiscoveryProtocol::Discv5 => self.is_discv5,
273 }
274 }
275
276 pub fn add_protocol(&mut self, protocol: DiscoveryProtocol) {
278 match protocol {
279 DiscoveryProtocol::Discv4 => self.is_discv4 = true,
280 DiscoveryProtocol::Discv5 => self.is_discv5 = true,
281 }
282 }
283}
284
285#[derive(Debug, Clone)]
286pub struct PeerData {
287 pub node: Node,
288 pub record: Option<NodeRecord>,
289 pub supported_capabilities: Vec<Capability>,
290 pub is_connection_inbound: bool,
293 pub connection: Option<PeerConnection>,
295 score: i64,
297 requests: i64,
299 pub last_response_time: Option<u64>,
301}
302
303impl PeerData {
304 pub fn new(
305 node: Node,
306 record: Option<NodeRecord>,
307 connection: Option<PeerConnection>,
308 capabilities: Vec<Capability>,
309 ) -> Self {
310 Self {
311 node,
312 record,
313 supported_capabilities: capabilities,
314 is_connection_inbound: false,
315 connection,
316 score: Default::default(),
317 requests: Default::default(),
318 last_response_time: None,
319 }
320 }
321}
322
323#[derive(Debug, Clone, serde::Serialize)]
325pub struct PeerDiagnostics {
326 pub peer_id: H256,
327 pub score: i64,
328 pub inflight_requests: i64,
329 pub eligible: bool,
330 pub capabilities: Vec<String>,
331 pub ip: IpAddr,
332 pub client_version: String,
333 pub connection_direction: String,
334 pub last_response_time: Option<u64>,
335}
336
337#[derive(Debug, Clone)]
339pub enum ContactValidation {
340 Valid(Box<Contact>),
341 InvalidContact,
342 UnknownContact,
343 IpMismatch,
344}
345
346#[must_use = "dropping this permit immediately releases the peer's request slot"]
358pub struct RequestPermit {
359 peer_table: PeerTable,
360 peer_id: H256,
361}
362
363impl RequestPermit {
364 pub(crate) fn new(peer_table: PeerTable, peer_id: H256) -> Self {
365 Self {
366 peer_table,
367 peer_id,
368 }
369 }
370}
371
372impl std::fmt::Debug for RequestPermit {
373 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374 f.debug_struct("RequestPermit")
375 .field("peer_id", &self.peer_id)
376 .finish_non_exhaustive()
377 }
378}
379
380impl Drop for RequestPermit {
381 fn drop(&mut self) {
382 let _ = self.peer_table.dec_requests(self.peer_id);
385 }
386}
387
388#[protocol]
389pub trait PeerTableServerProtocol: Send + Sync {
390 fn new_contacts(&self, nodes: Vec<Node>, protocol: DiscoveryProtocol)
392 -> Result<(), ActorError>;
393 fn new_contact_records(&self, node_records: Vec<NodeRecord>) -> Result<(), ActorError>;
394 fn new_connected_peer(
395 &self,
396 node: Node,
397 connection: PeerConnection,
398 capabilities: Vec<Capability>,
399 is_inbound: bool,
400 ) -> Result<(), ActorError>;
401 fn set_session_info(&self, node_id: H256, session: Session) -> Result<(), ActorError>;
402 fn remove_peer(&self, node_id: H256) -> Result<(), ActorError>;
403 fn dec_requests(&self, node_id: H256) -> Result<(), ActorError>;
404 fn set_unwanted(&self, node_id: H256) -> Result<(), ActorError>;
405 fn set_is_fork_id_valid(&self, node_id: H256, valid: bool) -> Result<(), ActorError>;
406 fn record_success(&self, node_id: H256) -> Result<(), ActorError>;
407 fn record_failure(&self, node_id: H256) -> Result<(), ActorError>;
408 fn record_critical_failure(&self, node_id: H256) -> Result<(), ActorError>;
409 fn record_ping_sent(&self, node_id: H256, ping_id: Bytes) -> Result<(), ActorError>;
410 fn record_pong_received(&self, node_id: H256, ping_id: Bytes) -> Result<(), ActorError>;
411 fn record_enr_request_sent(&self, node_id: H256, request_hash: H256) -> Result<(), ActorError>;
412 fn record_enr_response_received(
413 &self,
414 node_id: H256,
415 request_hash: H256,
416 record: NodeRecord,
417 ) -> Result<(), ActorError>;
418 fn set_disposable(&self, node_id: H256) -> Result<(), ActorError>;
419 fn mark_knows_us(&self, node_id: H256) -> Result<(), ActorError>;
420 fn prune_table(&self) -> Result<(), ActorError>;
421 fn shutdown(&self) -> Result<(), ActorError>;
422
423 fn peer_count(&self) -> Response<usize>;
425 fn peer_count_by_capabilities(&self, capabilities: Vec<Capability>) -> Response<usize>;
426 fn target_reached(&self) -> Response<bool>;
427 fn target_peers_reached(&self) -> Response<bool>;
428 fn target_peers_completion(&self) -> Response<f64>;
429 fn get_contact_to_initiate(&self) -> Response<Option<Box<Contact>>>;
430 fn get_contact_for_enr_lookup(&self) -> Response<Option<Box<Contact>>>;
431 fn get_closest_from_pool(&self, target: H256, count: usize) -> Response<Vec<(H256, Node)>>;
432 fn get_contact(&self, node_id: H256) -> Response<Option<Box<Contact>>>;
433 fn get_contact_to_revalidate(
434 &self,
435 revalidation_interval: Duration,
436 protocol: DiscoveryProtocol,
437 ) -> Response<Option<Box<Contact>>>;
438 fn get_best_peer(
439 &self,
440 capabilities: Vec<Capability>,
441 ) -> Response<Option<(H256, PeerConnection, RequestPermit)>>;
442 fn get_best_peer_excluding(
443 &self,
444 capabilities: Vec<Capability>,
445 excluded: Vec<H256>,
446 ) -> Response<Option<(H256, PeerConnection, RequestPermit)>>;
447 fn get_best_n_peers(
448 &self,
449 capabilities: Vec<Capability>,
450 n: usize,
451 ) -> Response<Vec<(H256, PeerConnection, RequestPermit)>>;
452 fn has_eligible_peer(&self, capabilities: Vec<Capability>) -> Response<bool>;
455 fn get_score(&self, node_id: H256) -> Response<i64>;
456 fn get_connected_nodes(&self) -> Response<Vec<Node>>;
457 fn get_peers_with_capabilities(&self)
458 -> Response<Vec<(H256, PeerConnection, Vec<Capability>)>>;
459 fn insert_if_new(&self, node: Node, protocol: DiscoveryProtocol) -> Response<bool>;
460 fn validate_contact(&self, node_id: H256, sender_ip: IpAddr) -> Response<ContactValidation>;
461 fn get_closest_nodes(&self, node_id: H256) -> Response<Vec<Node>>;
462 fn get_nodes_at_distances(&self, distances: Vec<u32>) -> Response<Vec<NodeRecord>>;
463 fn get_peers_data(&self) -> Response<Vec<PeerData>>;
464 fn get_random_peer(
465 &self,
466 capabilities: Vec<Capability>,
467 ) -> Response<Option<(H256, PeerConnection, RequestPermit)>>;
468 fn get_session_info(&self, node_id: H256) -> Response<Option<Session>>;
469 fn get_peer_diagnostics(&self) -> Response<Vec<PeerDiagnostics>>;
470 fn get_peer_connection(&self, peer_id: H256) -> Response<Option<PeerConnection>>;
471}
472
473#[derive(Debug)]
474pub struct PeerTableServer {
475 local_node_id: H256,
476 buckets: Vec<KBucket>,
477 peers: IndexMap<H256, PeerData>,
478 already_tried_peers: FxHashSet<H256>,
479 target_peers: usize,
480 store: Store,
481 sessions: FxHashMap<H256, Session>,
484 connection_pool: IndexMap<H256, Node>,
490}
491
492#[actor(protocol = PeerTableServerProtocol)]
493impl PeerTableServer {
494 pub fn spawn(local_node_id: H256, target_peers: usize, store: Store) -> PeerTable {
495 PeerTableServer::new(local_node_id, target_peers, store).start()
496 }
497
498 pub(crate) fn new(local_node_id: H256, target_peers: usize, store: Store) -> Self {
499 Self {
500 local_node_id,
501 buckets: vec![KBucket::default(); NUMBER_OF_BUCKETS],
502 peers: Default::default(),
503 already_tried_peers: Default::default(),
504 target_peers,
505 store,
506 sessions: Default::default(),
507 connection_pool: IndexMap::with_capacity(MAX_CONNECTION_POOL_SIZE),
508 }
509 }
510
511 #[started]
512 async fn started(&mut self, ctx: &Context<Self>) {
513 send_message_on(
514 ctx.clone(),
515 tokio::signal::ctrl_c(),
516 peer_table_server_protocol::Shutdown,
517 );
518 }
519
520 #[send_handler]
523 async fn handle_new_contacts(
524 &mut self,
525 msg: peer_table_server_protocol::NewContacts,
526 _ctx: &Context<Self>,
527 ) {
528 self.do_new_contacts(msg.nodes, msg.protocol).await;
529 }
530
531 #[send_handler]
532 async fn handle_new_contact_records(
533 &mut self,
534 msg: peer_table_server_protocol::NewContactRecords,
535 _ctx: &Context<Self>,
536 ) {
537 self.do_new_contact_records(msg.node_records).await;
538 }
539
540 #[send_handler]
541 async fn handle_new_connected_peer(
542 &mut self,
543 msg: peer_table_server_protocol::NewConnectedPeer,
544 _ctx: &Context<Self>,
545 ) {
546 let new_peer_id = msg.node.node_id();
547 let mut new_peer = PeerData::new(msg.node, None, Some(msg.connection), msg.capabilities);
548 new_peer.is_connection_inbound = msg.is_inbound;
549 self.peers.insert(new_peer_id, new_peer);
550 }
551
552 #[send_handler]
553 async fn handle_set_session_info(
554 &mut self,
555 msg: peer_table_server_protocol::SetSessionInfo,
556 _ctx: &Context<Self>,
557 ) {
558 self.sessions.insert(msg.node_id, msg.session.clone());
560 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
562 contact.session = Some(msg.session);
563 }
564 }
565
566 #[send_handler]
567 async fn handle_remove_peer(
568 &mut self,
569 msg: peer_table_server_protocol::RemovePeer,
570 _ctx: &Context<Self>,
571 ) {
572 self.peers.swap_remove(&msg.node_id);
573 self.sessions.remove(&msg.node_id);
576 }
577
578 #[send_handler]
579 async fn handle_dec_requests(
580 &mut self,
581 msg: peer_table_server_protocol::DecRequests,
582 _ctx: &Context<Self>,
583 ) {
584 self.peers.entry(msg.node_id).and_modify(|peer_data| {
585 if peer_data.requests <= 0 {
586 tracing::debug!(
590 peer_id = ?msg.node_id,
591 requests = peer_data.requests,
592 "dec_requests with counter already <= 0",
593 );
594 }
595 peer_data.requests = peer_data.requests.saturating_sub(1).max(0)
596 });
597 }
598
599 #[send_handler]
600 async fn handle_set_unwanted(
601 &mut self,
602 msg: peer_table_server_protocol::SetUnwanted,
603 _ctx: &Context<Self>,
604 ) {
605 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
606 contact.unwanted = true;
607 }
608 }
609
610 #[send_handler]
611 async fn handle_set_is_fork_id_valid(
612 &mut self,
613 msg: peer_table_server_protocol::SetIsForkIdValid,
614 _ctx: &Context<Self>,
615 ) {
616 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
617 contact.is_fork_id_valid = Some(msg.valid);
618 }
619 }
620
621 #[send_handler]
622 async fn handle_record_success(
623 &mut self,
624 msg: peer_table_server_protocol::RecordSuccess,
625 _ctx: &Context<Self>,
626 ) {
627 let now = std::time::SystemTime::now()
628 .duration_since(std::time::UNIX_EPOCH)
629 .unwrap_or_default()
630 .as_secs();
631 self.peers.entry(msg.node_id).and_modify(|peer_data| {
632 peer_data.score = (peer_data.score + 1).min(MAX_SCORE);
633 peer_data.last_response_time = Some(now);
634 });
635 }
636
637 #[send_handler]
638 async fn handle_record_failure(
639 &mut self,
640 msg: peer_table_server_protocol::RecordFailure,
641 _ctx: &Context<Self>,
642 ) {
643 self.peers
644 .entry(msg.node_id)
645 .and_modify(|peer_data| peer_data.score = (peer_data.score - 1).max(MIN_SCORE));
646 }
647
648 #[send_handler]
649 async fn handle_record_critical_failure(
650 &mut self,
651 msg: peer_table_server_protocol::RecordCriticalFailure,
652 _ctx: &Context<Self>,
653 ) {
654 self.peers
655 .entry(msg.node_id)
656 .and_modify(|peer_data| peer_data.score = MIN_SCORE_CRITICAL);
657 }
658
659 #[send_handler]
660 async fn handle_record_ping_sent(
661 &mut self,
662 msg: peer_table_server_protocol::RecordPingSent,
663 _ctx: &Context<Self>,
664 ) {
665 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
666 contact.record_ping_sent(msg.ping_id);
667 }
668 }
669
670 #[send_handler]
671 async fn handle_record_pong_received(
672 &mut self,
673 msg: peer_table_server_protocol::RecordPongReceived,
674 _ctx: &Context<Self>,
675 ) {
676 if let Some(contact) = self.get_contact_mut(&msg.node_id)
677 && contact
678 .ping_id
679 .as_ref()
680 .map(|value| *value == msg.ping_id)
681 .unwrap_or(false)
682 {
683 contact.ping_id = None;
684 }
685 }
686
687 #[send_handler]
688 async fn handle_record_enr_request_sent(
689 &mut self,
690 msg: peer_table_server_protocol::RecordEnrRequestSent,
691 _ctx: &Context<Self>,
692 ) {
693 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
694 contact.record_enr_request_sent(msg.request_hash);
695 }
696 }
697
698 #[send_handler]
699 async fn handle_record_enr_response_received(
700 &mut self,
701 msg: peer_table_server_protocol::RecordEnrResponseReceived,
702 _ctx: &Context<Self>,
703 ) {
704 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
705 contact.record_enr_response_received(msg.request_hash, msg.record);
706 }
707 }
708
709 #[send_handler]
710 async fn handle_set_disposable(
711 &mut self,
712 msg: peer_table_server_protocol::SetDisposable,
713 _ctx: &Context<Self>,
714 ) {
715 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
716 contact.disposable = true;
717 }
718 }
719
720 #[send_handler]
721 async fn handle_mark_knows_us(
722 &mut self,
723 msg: peer_table_server_protocol::MarkKnowsUs,
724 _ctx: &Context<Self>,
725 ) {
726 if let Some(contact) = self.get_contact_mut(&msg.node_id) {
727 contact.knows_us = true;
728 }
729 }
730
731 #[send_handler]
732 async fn handle_prune_table(
733 &mut self,
734 _msg: peer_table_server_protocol::PruneTable,
735 _ctx: &Context<Self>,
736 ) {
737 self.prune();
738 }
739
740 #[send_handler]
741 async fn handle_shutdown(
742 &mut self,
743 _msg: peer_table_server_protocol::Shutdown,
744 ctx: &Context<Self>,
745 ) {
746 ctx.stop();
747 }
748
749 #[request_handler]
752 async fn handle_peer_count(
753 &mut self,
754 _msg: peer_table_server_protocol::PeerCount,
755 _ctx: &Context<Self>,
756 ) -> usize {
757 self.peers.len()
758 }
759
760 #[request_handler]
761 async fn handle_peer_count_by_capabilities(
762 &mut self,
763 msg: peer_table_server_protocol::PeerCountByCapabilities,
764 _ctx: &Context<Self>,
765 ) -> usize {
766 self.do_peer_count_by_capabilities(msg.capabilities)
767 }
768
769 #[request_handler]
770 async fn handle_target_reached(
771 &mut self,
772 _msg: peer_table_server_protocol::TargetReached,
773 _ctx: &Context<Self>,
774 ) -> bool {
775 self.peers.len() >= self.target_peers
776 }
777
778 #[request_handler]
779 async fn handle_target_peers_reached(
780 &mut self,
781 _msg: peer_table_server_protocol::TargetPeersReached,
782 _ctx: &Context<Self>,
783 ) -> bool {
784 self.peers.len() >= self.target_peers
785 }
786
787 #[request_handler]
788 async fn handle_target_peers_completion(
789 &mut self,
790 _msg: peer_table_server_protocol::TargetPeersCompletion,
791 _ctx: &Context<Self>,
792 ) -> f64 {
793 self.peers.len() as f64 / self.target_peers as f64
794 }
795
796 #[request_handler]
797 async fn handle_get_contact_to_initiate(
798 &mut self,
799 _msg: peer_table_server_protocol::GetContactToInitiate,
800 _ctx: &Context<Self>,
801 ) -> Option<Box<Contact>> {
802 self.do_get_contact_to_initiate().map(Box::new)
803 }
804
805 #[request_handler]
806 async fn handle_get_closest_from_pool(
807 &mut self,
808 msg: peer_table_server_protocol::GetClosestFromPool,
809 _ctx: &Context<Self>,
810 ) -> Vec<(H256, Node)> {
811 self.do_get_closest_from_pool(msg.target, msg.count)
812 }
813
814 #[request_handler]
815 async fn handle_get_contact_for_enr_lookup(
816 &mut self,
817 _msg: peer_table_server_protocol::GetContactForEnrLookup,
818 _ctx: &Context<Self>,
819 ) -> Option<Box<Contact>> {
820 self.do_get_contact_for_enr_lookup().map(Box::new)
821 }
822
823 #[request_handler]
824 async fn handle_get_contact(
825 &mut self,
826 msg: peer_table_server_protocol::GetContact,
827 _ctx: &Context<Self>,
828 ) -> Option<Box<Contact>> {
829 self.get_contact(&msg.node_id).cloned().map(Box::new)
830 }
831
832 #[request_handler]
833 async fn handle_get_contact_to_revalidate(
834 &mut self,
835 msg: peer_table_server_protocol::GetContactToRevalidate,
836 _ctx: &Context<Self>,
837 ) -> Option<Box<Contact>> {
838 self.do_get_contact_to_revalidate(msg.revalidation_interval, msg.protocol)
839 }
840
841 #[request_handler]
842 async fn handle_get_best_peer(
843 &mut self,
844 msg: peer_table_server_protocol::GetBestPeer,
845 ctx: &Context<Self>,
846 ) -> Option<(H256, PeerConnection, RequestPermit)> {
847 let (peer_id, conn) = self.do_get_best_peer(&msg.capabilities)?;
848 self.peers
849 .get_mut(&peer_id)
850 .expect("peer returned by do_get_best_peer must be present in self.peers")
851 .requests += 1;
852 Some((peer_id, conn, RequestPermit::new(ctx.actor_ref(), peer_id)))
853 }
854
855 #[request_handler]
856 async fn handle_get_best_peer_excluding(
857 &mut self,
858 msg: peer_table_server_protocol::GetBestPeerExcluding,
859 ctx: &Context<Self>,
860 ) -> Option<(H256, PeerConnection, RequestPermit)> {
861 let (peer_id, conn) = self.do_get_best_peer_excluding(&msg.capabilities, &msg.excluded)?;
862 self.peers
863 .get_mut(&peer_id)
864 .expect("peer returned by do_get_best_peer_excluding must be present in self.peers")
865 .requests += 1;
866 Some((peer_id, conn, RequestPermit::new(ctx.actor_ref(), peer_id)))
867 }
868
869 #[request_handler]
870 async fn handle_get_best_n_peers(
871 &mut self,
872 msg: peer_table_server_protocol::GetBestNPeers,
873 ctx: &Context<Self>,
874 ) -> Vec<(H256, PeerConnection, RequestPermit)> {
875 let picks = self.do_get_best_n_peers(&msg.capabilities, msg.n);
876 let mut out = Vec::with_capacity(picks.len());
877 for (peer_id, conn) in picks {
878 self.peers
879 .get_mut(&peer_id)
880 .expect("peer returned by do_get_best_n_peers must be present in self.peers")
881 .requests += 1;
882 out.push((peer_id, conn, RequestPermit::new(ctx.actor_ref(), peer_id)));
883 }
884 out
885 }
886
887 #[request_handler]
888 async fn handle_has_eligible_peer(
889 &mut self,
890 msg: peer_table_server_protocol::HasEligiblePeer,
891 _ctx: &Context<Self>,
892 ) -> bool {
893 self.peers.values().any(|peer_data| {
894 peer_data.connection.is_some()
895 && self.can_try_more_requests(&peer_data.score, &peer_data.requests)
896 && msg
897 .capabilities
898 .iter()
899 .any(|cap| peer_data.supported_capabilities.contains(cap))
900 })
901 }
902
903 #[request_handler]
904 async fn handle_get_score(
905 &mut self,
906 msg: peer_table_server_protocol::GetScore,
907 _ctx: &Context<Self>,
908 ) -> i64 {
909 self.peers
910 .get(&msg.node_id)
911 .map(|peer_data| peer_data.score)
912 .unwrap_or_default()
913 }
914
915 #[request_handler]
916 async fn handle_get_connected_nodes(
917 &mut self,
918 _msg: peer_table_server_protocol::GetConnectedNodes,
919 _ctx: &Context<Self>,
920 ) -> Vec<Node> {
921 self.peers
922 .values()
923 .map(|peer_data| peer_data.node.clone())
924 .collect()
925 }
926
927 #[request_handler]
928 async fn handle_get_peers_with_capabilities(
929 &mut self,
930 _msg: peer_table_server_protocol::GetPeersWithCapabilities,
931 _ctx: &Context<Self>,
932 ) -> Vec<(H256, PeerConnection, Vec<Capability>)> {
933 self.peers
934 .iter()
935 .filter_map(|(peer_id, peer_data)| {
936 peer_data.connection.clone().map(|connection| {
937 (
938 *peer_id,
939 connection,
940 peer_data.supported_capabilities.clone(),
941 )
942 })
943 })
944 .collect()
945 }
946
947 #[request_handler]
948 async fn handle_insert_if_new(
949 &mut self,
950 msg: peer_table_server_protocol::InsertIfNew,
951 _ctx: &Context<Self>,
952 ) -> bool {
953 let node_id = msg.node.node_id();
954 self.insert_to_connection_pool(node_id, msg.node.clone());
956 if self.contact_exists(&node_id) {
957 return false;
958 }
959 let contact = Contact::new(msg.node, msg.protocol);
960 self.insert_contact(node_id, contact);
964 METRICS.record_new_discovery().await;
965 true
966 }
967
968 #[request_handler]
969 async fn handle_validate_contact(
970 &mut self,
971 msg: peer_table_server_protocol::ValidateContact,
972 _ctx: &Context<Self>,
973 ) -> ContactValidation {
974 self.do_validate_contact(msg.node_id, msg.sender_ip)
975 }
976
977 #[request_handler]
978 async fn handle_get_closest_nodes(
979 &mut self,
980 msg: peer_table_server_protocol::GetClosestNodes,
981 _ctx: &Context<Self>,
982 ) -> Vec<Node> {
983 self.do_get_closest_nodes(msg.node_id)
984 }
985
986 #[request_handler]
987 async fn handle_get_nodes_at_distances(
988 &mut self,
989 msg: peer_table_server_protocol::GetNodesAtDistances,
990 _ctx: &Context<Self>,
991 ) -> Vec<NodeRecord> {
992 self.do_get_nodes_at_distances(&msg.distances)
993 }
994
995 #[request_handler]
996 async fn handle_get_peers_data(
997 &mut self,
998 _msg: peer_table_server_protocol::GetPeersData,
999 _ctx: &Context<Self>,
1000 ) -> Vec<PeerData> {
1001 self.peers.values().cloned().collect()
1002 }
1003
1004 #[request_handler]
1005 async fn handle_get_random_peer(
1006 &mut self,
1007 msg: peer_table_server_protocol::GetRandomPeer,
1008 ctx: &Context<Self>,
1009 ) -> Option<(H256, PeerConnection, RequestPermit)> {
1010 let (peer_id, conn) = self.do_get_random_peer(msg.capabilities)?;
1011 self.peers
1012 .get_mut(&peer_id)
1013 .expect("peer returned by do_get_random_peer must be present in self.peers")
1014 .requests += 1;
1015 Some((peer_id, conn, RequestPermit::new(ctx.actor_ref(), peer_id)))
1016 }
1017
1018 #[request_handler]
1019 async fn handle_get_session_info(
1020 &mut self,
1021 msg: peer_table_server_protocol::GetSessionInfo,
1022 _ctx: &Context<Self>,
1023 ) -> Option<Session> {
1024 self.sessions
1026 .get(&msg.node_id)
1027 .cloned()
1028 .or_else(|| self.get_contact(&msg.node_id)?.session.clone())
1029 }
1030
1031 #[request_handler]
1032 async fn handle_get_peer_connection(
1033 &mut self,
1034 msg: peer_table_server_protocol::GetPeerConnection,
1035 _ctx: &Context<Self>,
1036 ) -> Option<PeerConnection> {
1037 self.peers
1038 .get(&msg.peer_id)
1039 .and_then(|peer_data| peer_data.connection.clone())
1040 }
1041
1042 #[request_handler]
1043 async fn handle_get_peer_diagnostics(
1044 &mut self,
1045 _msg: peer_table_server_protocol::GetPeerDiagnostics,
1046 _ctx: &Context<Self>,
1047 ) -> Vec<PeerDiagnostics> {
1048 self.peers
1049 .iter()
1050 .map(|(id, peer_data)| PeerDiagnostics {
1051 peer_id: *id,
1052 score: peer_data.score,
1053 inflight_requests: peer_data.requests,
1054 eligible: self.can_try_more_requests(&peer_data.score, &peer_data.requests),
1055 capabilities: peer_data
1056 .supported_capabilities
1057 .iter()
1058 .map(|c| format!("{}/{}", c.protocol(), c.version))
1059 .collect(),
1060 ip: peer_data.node.ip,
1061 client_version: peer_data.node.version.clone().unwrap_or_default(),
1062 connection_direction: if peer_data.is_connection_inbound {
1063 "inbound".to_string()
1064 } else {
1065 "outbound".to_string()
1066 },
1067 last_response_time: peer_data.last_response_time,
1068 })
1069 .collect()
1070 }
1071
1072 fn bucket_for(&self, node_id: &H256) -> Option<usize> {
1078 bucket_index(&self.local_node_id, node_id)
1079 }
1080
1081 fn get_contact(&self, node_id: &H256) -> Option<&Contact> {
1083 let idx = self.bucket_for(node_id)?;
1084 self.buckets[idx].get_any(node_id)
1085 }
1086
1087 fn get_contact_mut(&mut self, node_id: &H256) -> Option<&mut Contact> {
1089 let idx = self.bucket_for(node_id)?;
1090 self.buckets[idx].get_mut(node_id)
1091 }
1092
1093 fn contact_exists(&self, node_id: &H256) -> bool {
1095 let Some(idx) = self.bucket_for(node_id) else {
1096 return false;
1097 };
1098 self.buckets[idx].contains(node_id)
1099 }
1100
1101 fn insert_contact(&mut self, node_id: H256, contact: Contact) -> bool {
1105 #[cfg(feature = "metrics")]
1106 let start = std::time::Instant::now();
1107
1108 let Some(idx) = self.bucket_for(&node_id) else {
1109 return false;
1110 };
1111 let result = self.buckets[idx].insert(node_id, contact);
1112
1113 #[cfg(feature = "metrics")]
1114 {
1115 use ethrex_metrics::p2p::METRICS_P2P;
1116 METRICS_P2P.observe_insert_contact_duration(start.elapsed().as_secs_f64());
1117 }
1118
1119 result
1120 }
1121
1122 fn insert_to_connection_pool(&mut self, node_id: H256, node: Node) {
1125 if self.connection_pool.contains_key(&node_id) {
1126 return;
1127 }
1128 if self.connection_pool.len() >= MAX_CONNECTION_POOL_SIZE {
1129 self.connection_pool.shift_remove_index(0);
1130 }
1131 self.connection_pool.insert(node_id, node);
1132 }
1133
1134 fn get_contact_or_replacement(&self, node_id: &H256) -> Option<&Contact> {
1136 let idx = self.bucket_for(node_id)?;
1137 self.buckets[idx].get_any(node_id)
1138 }
1139
1140 fn get_contact_or_replacement_mut(&mut self, node_id: &H256) -> Option<&mut Contact> {
1142 let idx = self.bucket_for(node_id)?;
1143 let bucket = &mut self.buckets[idx];
1144 if let Some(pos) = bucket.contacts.iter().position(|(id, _)| id == node_id) {
1147 return Some(&mut bucket.contacts[pos].1);
1148 }
1149 if let Some(pos) = bucket.replacements.iter().position(|(id, _)| id == node_id) {
1150 return Some(&mut bucket.replacements[pos].1);
1151 }
1152 None
1153 }
1154
1155 fn iter_contacts(&self) -> impl Iterator<Item = (&H256, &Contact)> {
1157 self.buckets.iter().flat_map(|bucket| {
1158 bucket
1159 .contacts
1160 .iter()
1161 .chain(bucket.replacements.iter())
1162 .map(|(id, c)| (id, c))
1163 })
1164 }
1165
1166 fn weight_peer(&self, score: &i64, requests: &i64) -> i64 {
1169 score * SCORE_WEIGHT - requests * REQUESTS_WEIGHT
1170 }
1171
1172 fn can_try_more_requests(&self, score: &i64, requests: &i64) -> bool {
1173 let score_ratio = (score - MIN_SCORE) as f64 / (MAX_SCORE - MIN_SCORE) as f64;
1174 let max_requests = (MAX_CONCURRENT_REQUESTS_PER_PEER as f64 * score_ratio).max(1.0);
1175 (*requests as f64) < max_requests
1176 }
1177
1178 fn do_get_best_peer(&self, capabilities: &[Capability]) -> Option<(H256, PeerConnection)> {
1179 self.do_get_best_peer_excluding(capabilities, &[])
1180 }
1181
1182 fn do_get_best_peer_excluding(
1185 &self,
1186 capabilities: &[Capability],
1187 excluded: &[H256],
1188 ) -> Option<(H256, PeerConnection)> {
1189 self.peers
1190 .iter()
1191 .filter_map(|(id, peer_data)| {
1192 if excluded.contains(id)
1193 || !self.can_try_more_requests(&peer_data.score, &peer_data.requests)
1194 || !capabilities
1195 .iter()
1196 .any(|cap| peer_data.supported_capabilities.contains(cap))
1197 {
1198 None
1199 } else {
1200 let connection = peer_data.connection.clone()?;
1201 Some((*id, peer_data.score, peer_data.requests, connection))
1202 }
1203 })
1204 .max_by_key(|(_, score, reqs, _)| self.weight_peer(score, reqs))
1205 .map(|(k, _, _, v)| (k, v))
1206 }
1207
1208 fn do_get_best_n_peers(
1214 &self,
1215 capabilities: &[Capability],
1216 n: usize,
1217 ) -> Vec<(H256, PeerConnection)> {
1218 let mut candidates: Vec<(H256, i64, i64, PeerConnection)> = self
1219 .peers
1220 .iter()
1221 .filter_map(|(id, peer_data)| {
1222 if !self.can_try_more_requests(&peer_data.score, &peer_data.requests)
1223 || !capabilities
1224 .iter()
1225 .any(|cap| peer_data.supported_capabilities.contains(cap))
1226 {
1227 None
1228 } else {
1229 let connection = peer_data.connection.clone()?;
1230 Some((*id, peer_data.score, peer_data.requests, connection))
1231 }
1232 })
1233 .collect();
1234
1235 candidates.sort_by_key(|(_, score, reqs, _)| -self.weight_peer(score, reqs));
1236 candidates
1237 .into_iter()
1238 .take(n)
1239 .map(|(id, _, _, conn)| (id, conn))
1240 .collect()
1241 }
1242
1243 fn prune(&mut self) {
1250 for bucket in &mut self.buckets {
1251 let main_disposable: Vec<H256> = bucket
1253 .contacts
1254 .iter()
1255 .filter(|(_, c)| c.disposable)
1256 .map(|(id, _)| *id)
1257 .collect();
1258
1259 for node_id in main_disposable {
1261 bucket.remove_and_promote(&node_id);
1262 }
1263
1264 bucket.replacements.retain(|(_, c)| !c.disposable);
1267 }
1268 }
1269
1270 fn do_get_contact_to_initiate(&mut self) -> Option<Contact> {
1271 let pool_len = self.connection_pool.len();
1275 if pool_len == 0 {
1276 return None;
1277 }
1278
1279 let start = rand::random::<usize>() % pool_len;
1280 for offset in 0..pool_len {
1281 let idx = (start + offset) % pool_len;
1282 let Some((node_id, node)) = self.connection_pool.get_index(idx) else {
1283 continue;
1284 };
1285 let node_id = *node_id;
1286
1287 if self.peers.contains_key(&node_id)
1288 || self.already_tried_peers.contains(&node_id)
1289 || self
1290 .get_contact_or_replacement(&node_id)
1291 .map(|c| !c.knows_us || c.unwanted || c.is_fork_id_valid == Some(false))
1292 .unwrap_or(false)
1293 {
1294 continue;
1295 }
1296
1297 let node = node.clone();
1298 self.already_tried_peers.insert(node_id);
1299 let contact = self
1300 .get_contact_or_replacement(&node_id)
1301 .cloned()
1302 .unwrap_or_else(|| Contact::new(node, DiscoveryProtocol::Discv4));
1303 return Some(contact);
1304 }
1305
1306 tracing::trace!("Resetting list of tried peers.");
1308 self.already_tried_peers.clear();
1309 None
1310 }
1311
1312 fn do_get_closest_from_pool(&self, target: H256, count: usize) -> Vec<(H256, Node)> {
1314 let mut nodes: Vec<(H256, Node, H256)> = Vec::with_capacity(count);
1315
1316 for (node_id, node) in &self.connection_pool {
1317 let dist = xor_distance(&target, node_id);
1318 if nodes.len() < count {
1319 nodes.push((*node_id, node.clone(), dist));
1320 } else if let Some((farthest_idx, _)) =
1321 nodes.iter().enumerate().max_by_key(|(_, (_, _, d))| *d)
1322 && dist < nodes[farthest_idx].2
1323 {
1324 nodes[farthest_idx] = (*node_id, node.clone(), dist);
1325 }
1326 }
1327
1328 nodes.sort_by(|a, b| a.2.cmp(&b.2));
1329 nodes.into_iter().map(|(id, node, _)| (id, node)).collect()
1330 }
1331
1332 fn do_get_contact_for_enr_lookup(&mut self) -> Option<Contact> {
1334 self.iter_contacts()
1335 .filter(|(_, c)| {
1336 c.is_discv4
1337 && c.was_validated()
1338 && !c.has_pending_enr_request()
1339 && c.record.is_none()
1340 && !c.disposable
1341 })
1342 .map(|(_, c)| c)
1343 .collect::<Vec<_>>()
1344 .choose(&mut rand::rngs::OsRng)
1345 .cloned()
1346 .cloned()
1347 }
1348
1349 fn do_get_contact_to_revalidate(
1350 &self,
1351 revalidation_interval: Duration,
1352 protocol: DiscoveryProtocol,
1353 ) -> Option<Box<Contact>> {
1354 self.iter_contacts()
1355 .filter(|(_, c)| {
1356 c.supports_protocol(protocol)
1357 && Self::is_validation_needed(c, revalidation_interval)
1358 })
1359 .map(|(_, c)| c)
1360 .choose(&mut rand::rngs::OsRng)
1361 .cloned()
1362 .map(Box::new)
1363 }
1364
1365 fn do_validate_contact(&self, node_id: H256, sender_ip: IpAddr) -> ContactValidation {
1366 let Some(contact) = self.get_contact(&node_id) else {
1367 return ContactValidation::UnknownContact;
1368 };
1369 if !contact.was_validated() {
1370 return ContactValidation::InvalidContact;
1371 }
1372
1373 if sender_ip != contact.node.ip {
1376 return ContactValidation::IpMismatch;
1377 }
1378 ContactValidation::Valid(Box::new(contact.clone()))
1379 }
1380
1381 fn do_get_closest_nodes(&self, node_id: H256) -> Vec<Node> {
1383 #[cfg(feature = "metrics")]
1384 let scan_start = std::time::Instant::now();
1385
1386 let mut nodes: Vec<(Node, H256)> = vec![];
1387
1388 for (contact_id, contact) in self.iter_contacts() {
1389 let dist = xor_distance(&node_id, contact_id);
1390 if nodes.len() < MAX_NODES_IN_NEIGHBORS_PACKET {
1391 nodes.push((contact.node.clone(), dist));
1392 } else if let Some((farthest_idx, _)) =
1393 nodes.iter().enumerate().max_by_key(|(_, (_, d))| *d)
1394 && dist < nodes[farthest_idx].1
1395 {
1396 nodes[farthest_idx] = (contact.node.clone(), dist);
1397 }
1398 }
1399
1400 #[cfg(feature = "metrics")]
1401 {
1402 use ethrex_metrics::p2p::METRICS_P2P;
1403 METRICS_P2P.observe_iter_contacts_duration(scan_start.elapsed().as_secs_f64());
1404 }
1405
1406 nodes.into_iter().map(|(node, _)| node).collect()
1407 }
1408
1409 fn do_get_nodes_at_distances(&self, distances: &[u32]) -> Vec<NodeRecord> {
1414 self.iter_contacts()
1415 .filter_map(|(contact_id, contact)| {
1416 let dist = distance(&self.local_node_id, contact_id) as u32;
1417 if distances.contains(&dist) {
1418 contact.record.clone()
1419 } else {
1420 None
1421 }
1422 })
1423 .take(MAX_ENRS_PER_FINDNODE_RESPONSE)
1424 .collect()
1425 }
1426
1427 async fn do_new_contacts(&mut self, nodes: Vec<Node>, protocol: DiscoveryProtocol) {
1428 for node in nodes {
1429 let node_id = node.node_id();
1430 if node_id == self.local_node_id {
1431 continue;
1432 }
1433 #[cfg(feature = "metrics")]
1434 let insert_start = std::time::Instant::now();
1435
1436 self.insert_to_connection_pool(node_id, node.clone());
1438
1439 if self.contact_exists(&node_id) {
1440 if let Some(contact) = self.get_contact_or_replacement_mut(&node_id) {
1442 contact.add_protocol(protocol);
1443 }
1444 } else {
1445 let contact = Contact::new(node, protocol);
1446 self.insert_contact(node_id, contact);
1447 METRICS.record_new_discovery().await;
1448 }
1449
1450 #[cfg(feature = "metrics")]
1451 {
1452 use ethrex_metrics::p2p::METRICS_P2P;
1453 METRICS_P2P.observe_insert_contact_duration(insert_start.elapsed().as_secs_f64());
1454 }
1455 }
1456 }
1457
1458 async fn do_new_contact_records(&mut self, node_records: Vec<NodeRecord>) {
1459 for node_record in node_records {
1460 if !node_record.verify_signature() {
1461 continue;
1462 }
1463 if let Ok(node) = Node::from_enr(&node_record) {
1464 let node_id = node.node_id();
1465 if node_id == self.local_node_id {
1466 continue;
1467 }
1468
1469 self.insert_to_connection_pool(node_id, node.clone());
1471
1472 if self.contact_exists(&node_id) {
1473 let should_update = self
1476 .get_contact_or_replacement(&node_id)
1477 .map(|c| match c.record.as_ref() {
1478 None => true,
1479 Some(r) => node_record.seq > r.seq,
1480 })
1481 .unwrap_or(false);
1482 let is_fork_id_valid = if should_update {
1483 Self::evaluate_fork_id(&node_record, &self.store).await
1484 } else {
1485 None
1486 };
1487 if let Some(contact) = self.get_contact_or_replacement_mut(&node_id) {
1488 contact.add_protocol(DiscoveryProtocol::Discv5);
1489 if should_update {
1490 if contact.node.ip != node.ip || contact.node.udp_port != node.udp_port
1491 {
1492 contact.validation_timestamp = None;
1493 contact.ping_id = None;
1494 }
1495 contact.node = node;
1496 contact.record = Some(node_record);
1497 contact.is_fork_id_valid = is_fork_id_valid;
1498 }
1499 }
1500 } else {
1501 let is_fork_id_valid = Self::evaluate_fork_id(&node_record, &self.store).await;
1502 let mut contact = Contact::new(node, DiscoveryProtocol::Discv5);
1503 contact.is_fork_id_valid = is_fork_id_valid;
1504 contact.record = Some(node_record);
1505 self.insert_contact(node_id, contact);
1506 METRICS.record_new_discovery().await;
1507 }
1508 }
1509 }
1510 }
1511
1512 async fn evaluate_fork_id(record: &NodeRecord, store: &Store) -> Option<bool> {
1513 if let Some(remote_fork_id) = record.get_fork_id() {
1514 backend::is_fork_id_valid(store, remote_fork_id)
1515 .await
1516 .ok()
1517 .or(Some(false))
1518 } else {
1519 Some(false)
1520 }
1521 }
1522
1523 fn do_peer_count_by_capabilities(&self, capabilities: Vec<Capability>) -> usize {
1524 self.peers
1525 .values()
1526 .filter(|peer_data| {
1527 capabilities
1528 .iter()
1529 .any(|cap| peer_data.supported_capabilities.contains(cap))
1530 })
1531 .count()
1532 }
1533
1534 fn do_get_random_peer(&self, capabilities: Vec<Capability>) -> Option<(H256, PeerConnection)> {
1535 let peers: Vec<(H256, &PeerConnection, i64)> = self
1536 .peers
1537 .iter()
1538 .filter_map(|(node_id, peer_data)| {
1539 if !capabilities
1540 .iter()
1541 .any(|cap| peer_data.supported_capabilities.contains(cap))
1542 {
1543 return None;
1544 }
1545 peer_data
1546 .connection
1547 .as_ref()
1548 .map(|connection| (*node_id, connection, peer_data.score))
1549 })
1550 .collect();
1551 if peers.is_empty() {
1552 return None;
1553 }
1554 let weights: Vec<u64> = peers
1556 .iter()
1557 .map(|(_, _, score)| (score.max(&MIN_SCORE_CRITICAL) - MIN_SCORE_CRITICAL + 1) as u64)
1558 .collect();
1559 let dist = WeightedIndex::new(&weights).ok()?;
1560 let idx = dist.sample(&mut rand::rngs::OsRng);
1561 Some((peers[idx].0, peers[idx].1.clone()))
1562 }
1563
1564 fn is_validation_needed(contact: &Contact, revalidation_interval: Duration) -> bool {
1565 if contact.disposable {
1566 return false;
1567 }
1568
1569 let sent_ping_ttl = Duration::from_secs(30);
1570
1571 if contact.has_pending_ping() {
1572 contact
1574 .validation_timestamp
1575 .map(|ts| Instant::now().saturating_duration_since(ts) > sent_ping_ttl)
1576 .unwrap_or(false)
1577 } else {
1578 !contact.was_validated()
1580 || contact
1581 .validation_timestamp
1582 .map(|ts| Instant::now().saturating_duration_since(ts) > revalidation_interval)
1583 .unwrap_or(false)
1584 }
1585 }
1586}
1587
1588pub type PeerTable = ActorRef<PeerTableServer>;
1589
1590#[cfg(test)]
1591mod tests {
1592 use super::*;
1593 use ethrex_common::H512;
1594 use std::net::Ipv4Addr;
1595
1596 fn dummy_contact(seed: u8) -> (H256, Contact) {
1598 let pk = H512::from_low_u64_be(seed as u64 + 1);
1599 let node = Node::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, seed)), 30303, 30303, pk);
1600 let node_id = node.node_id();
1601 let contact = Contact::new(node, DiscoveryProtocol::Discv4);
1602 (node_id, contact)
1603 }
1604
1605 #[test]
1608 fn insert_into_empty_bucket() {
1609 let mut bucket = KBucket::default();
1610 let (id, contact) = dummy_contact(1);
1611 assert!(bucket.insert(id, contact));
1612 assert_eq!(bucket.contacts.len(), 1);
1613 assert!(bucket.replacements.is_empty());
1614 }
1615
1616 #[test]
1617 fn insert_fills_bucket_then_goes_to_replacements() {
1618 let mut bucket = KBucket::default();
1619
1620 for i in 0..MAX_NODES_PER_BUCKET as u8 {
1622 let (id, contact) = dummy_contact(i);
1623 assert!(bucket.insert(id, contact), "contact {i} should go to main");
1624 }
1625 assert_eq!(bucket.contacts.len(), MAX_NODES_PER_BUCKET);
1626
1627 let (id, contact) = dummy_contact(200);
1629 assert!(!bucket.insert(id, contact));
1630 assert_eq!(bucket.contacts.len(), MAX_NODES_PER_BUCKET);
1631 assert_eq!(bucket.replacements.len(), 1);
1632 }
1633
1634 #[test]
1637 fn contains_checks_main_and_replacement() {
1638 let mut bucket = KBucket::default();
1639
1640 let (id_main, contact_main) = dummy_contact(1);
1641 bucket.insert(id_main, contact_main);
1642 assert!(bucket.contains(&id_main));
1643
1644 for i in 2..=(MAX_NODES_PER_BUCKET as u8) {
1646 let (id, c) = dummy_contact(i);
1647 bucket.insert(id, c);
1648 }
1649 let (id_repl, contact_repl) = dummy_contact(100);
1650 bucket.insert(id_repl, contact_repl);
1651
1652 assert!(bucket.contains(&id_repl));
1653 assert!(!bucket.contains(&H256::zero()));
1654 }
1655
1656 #[test]
1659 fn get_returns_main_list_only() {
1660 let mut bucket = KBucket::default();
1661 let (id, contact) = dummy_contact(1);
1662 bucket.insert(id, contact);
1663 assert!(bucket.get(&id).is_some());
1664 assert!(bucket.get(&H256::zero()).is_none());
1665 }
1666
1667 #[test]
1668 fn get_any_returns_from_replacement() {
1669 let mut bucket = KBucket::default();
1670 for i in 0..MAX_NODES_PER_BUCKET as u8 {
1672 let (id, c) = dummy_contact(i);
1673 bucket.insert(id, c);
1674 }
1675 let (id_repl, c_repl) = dummy_contact(200);
1677 bucket.insert(id_repl, c_repl);
1678
1679 assert!(bucket.get(&id_repl).is_none()); assert!(bucket.get_any(&id_repl).is_some()); }
1682
1683 #[test]
1686 fn remove_and_promote_with_replacement() {
1687 let mut bucket = KBucket::default();
1688
1689 let mut main_ids = Vec::new();
1691 for i in 0..MAX_NODES_PER_BUCKET as u8 {
1692 let (id, c) = dummy_contact(i);
1693 main_ids.push(id);
1694 bucket.insert(id, c);
1695 }
1696
1697 let (repl_id, repl_contact) = dummy_contact(200);
1699 bucket.insert(repl_id, repl_contact);
1700
1701 let promoted = bucket.remove_and_promote(&main_ids[0]);
1703 assert_eq!(promoted, Some(repl_id));
1704 assert_eq!(bucket.contacts.len(), MAX_NODES_PER_BUCKET);
1705 assert!(bucket.replacements.is_empty());
1706 assert!(!bucket.contains(&main_ids[0]));
1707 assert!(bucket.contains(&repl_id));
1708 }
1709
1710 #[test]
1711 fn remove_and_promote_without_replacement() {
1712 let mut bucket = KBucket::default();
1713 let (id, c) = dummy_contact(1);
1714 bucket.insert(id, c);
1715
1716 let promoted = bucket.remove_and_promote(&id);
1717 assert!(promoted.is_none());
1718 assert!(bucket.contacts.is_empty());
1719 }
1720
1721 #[test]
1722 fn remove_nonexistent_returns_none() {
1723 let mut bucket = KBucket::default();
1724 assert!(bucket.remove_and_promote(&H256::zero()).is_none());
1725 }
1726
1727 #[test]
1730 fn replacement_list_evicts_oldest_when_full() {
1731 let mut bucket = KBucket::default();
1732 for i in 0..MAX_NODES_PER_BUCKET as u8 {
1734 let (id, c) = dummy_contact(i);
1735 bucket.insert(id, c);
1736 }
1737
1738 let mut repl_ids = Vec::new();
1740 for i in 0..(MAX_REPLACEMENTS_PER_BUCKET + 2) as u8 {
1741 let seed = 100 + i;
1742 let (id, c) = dummy_contact(seed);
1743 repl_ids.push(id);
1744 bucket.insert(id, c);
1745 }
1746
1747 assert_eq!(bucket.replacements.len(), MAX_REPLACEMENTS_PER_BUCKET);
1748 assert!(!bucket.contains(&repl_ids[0]));
1750 assert!(!bucket.contains(&repl_ids[1]));
1751 assert!(bucket.contains(repl_ids.last().unwrap()));
1753 }
1754
1755 #[test]
1758 fn bucket_index_self_is_none() {
1759 let id = H256::random();
1760 assert_eq!(bucket_index(&id, &id), None);
1761 }
1762
1763 #[test]
1764 fn bucket_index_minimal_distance() {
1765 let local = H256::zero();
1766 let mut remote = H256::zero();
1768 remote.0[31] = 1;
1769 assert_eq!(bucket_index(&local, &remote), Some(0));
1770 }
1771
1772 #[test]
1773 fn bucket_index_maximal_distance() {
1774 let local = H256::zero();
1775 let mut remote = H256::zero();
1777 remote.0[0] = 0x80;
1778 assert_eq!(bucket_index(&local, &remote), Some(255));
1779 }
1780}