1#[cfg(feature = "telemetry")]
17use crate::helpers::Telemetry;
18use crate::{
19 CONTEXT,
20 MAX_BATCH_DELAY,
21 MEMORY_POOL_PORT,
22 Worker,
23 events::{DisconnectReason, EventCodec, PrimaryPing},
24 helpers::{Cache, PrimarySender, Storage, SyncSender, WorkerSender, assign_to_worker},
25 spawn_blocking,
26};
27use smol_str::SmolStr;
28use snarkos_account::Account;
29use snarkos_node_bft_events::{
30 BlockRequest,
31 BlockResponse,
32 CertificateRequest,
33 CertificateResponse,
34 ChallengeRequest,
35 ChallengeResponse,
36 DataBlocks,
37 Event,
38 EventTrait,
39 TransmissionRequest,
40 TransmissionResponse,
41 ValidatorsRequest,
42 ValidatorsResponse,
43};
44use snarkos_node_bft_ledger_service::LedgerService;
45use snarkos_node_network::{
46 ConnectionMode,
47 NodeType,
48 Peer,
49 PeerPoolHandling,
50 Resolver,
51 bootstrap_peers,
52 get_repo_commit_hash,
53 log_repo_sha_comparison,
54 shorten_snarkos_sha,
55};
56use snarkos_node_sync::{MAX_BLOCKS_BEHIND, communication_service::CommunicationService};
57use snarkos_node_tcp::{
58 Config,
59 ConnectError,
60 Connection,
61 ConnectionSide,
62 P2P,
63 Tcp,
64 connections::DisconnectOrigin,
65 protocols::{Disconnect, Handshake, OnConnect, Reading, Writing},
66};
67use snarkos_utilities::NodeDataDir;
68use snarkvm::{
69 console::prelude::*,
70 ledger::{
71 committee::Committee,
72 narwhal::{BatchHeader, Data},
73 },
74 prelude::{Address, Field},
75 utilities::flatten_error,
76};
77
78use colored::Colorize;
79use futures::{SinkExt, future::join_all};
80use indexmap::IndexMap;
81#[cfg(feature = "locktick")]
82use locktick::parking_lot::{Mutex, RwLock};
83#[cfg(not(feature = "locktick"))]
84use parking_lot::{Mutex, RwLock};
85use rand::seq::{IteratorRandom, SliceRandom};
86use std::{
87 collections::{HashMap, HashSet},
88 future::Future,
89 io,
90 net::{Ipv4Addr, SocketAddr, SocketAddrV4},
91 sync::Arc,
92 time::Duration,
93};
94use tokio::{
95 net::TcpStream,
96 sync::{OnceCell, oneshot},
97 task::{self, JoinHandle},
98};
99use tokio_stream::StreamExt;
100use tokio_util::codec::Framed;
101
102const CACHE_EVENTS_INTERVAL: i64 = (MAX_BATCH_DELAY.as_secs()) as i64; const CACHE_REQUESTS_INTERVAL: i64 = (MAX_BATCH_DELAY.as_secs()) as i64; #[cfg(not(test))]
109const MAX_CONNECTION_ATTEMPTS: usize = 10;
110
111pub const MAX_VALIDATORS_TO_SEND: usize = 200;
113
114#[cfg(not(test))]
116const CONNECTION_ATTEMPTS_SINCE_SECS: i64 = 10;
117
118const IP_BAN_TIME_IN_SECS: u64 = 300;
120
121#[async_trait]
124pub trait Transport<N: Network>: Send + Sync {
125 async fn send(&self, peer_ip: SocketAddr, event: Event<N>) -> Option<oneshot::Receiver<io::Result<()>>>;
126 fn broadcast(&self, event: Event<N>);
127}
128
129#[derive(Clone)]
132pub struct Gateway<N: Network>(Arc<InnerGateway<N>>);
133
134impl<N: Network> Deref for Gateway<N> {
135 type Target = Arc<InnerGateway<N>>;
136
137 fn deref(&self) -> &Self::Target {
138 &self.0
139 }
140}
141
142pub struct InnerGateway<N: Network> {
143 account: Account<N>,
145 storage: Storage<N>,
147 ledger: Arc<dyn LedgerService<N>>,
149 tcp: Tcp,
151 cache: Cache<N>,
153 resolver: RwLock<Resolver<N>>,
155 peer_pool: RwLock<HashMap<SocketAddr, Peer<N>>>,
157 #[cfg(feature = "telemetry")]
158 validator_telemetry: Telemetry<N>,
159 primary_sender: OnceCell<PrimarySender<N>>,
161 worker_senders: OnceCell<IndexMap<u8, WorkerSender<N>>>,
163 sync_sender: OnceCell<SyncSender<N>>,
165 handles: Mutex<Vec<JoinHandle<()>>>,
167 node_data_dir: NodeDataDir,
169 trusted_peers_only: bool,
171 dev: Option<u16>,
173}
174
175impl<N: Network> PeerPoolHandling<N> for Gateway<N> {
176 const MAXIMUM_POOL_SIZE: usize = 200;
177 const OWNER: &str = CONTEXT;
178 const PEER_SLASHING_COUNT: usize = 20;
179
180 fn peer_pool(&self) -> &RwLock<HashMap<SocketAddr, Peer<N>>> {
181 &self.peer_pool
182 }
183
184 fn resolver(&self) -> &RwLock<Resolver<N>> {
185 &self.resolver
186 }
187
188 fn is_dev(&self) -> bool {
189 self.dev.is_some()
190 }
191
192 fn trusted_peers_only(&self) -> bool {
193 self.trusted_peers_only
194 }
195
196 fn node_type(&self) -> NodeType {
197 NodeType::Validator
198 }
199}
200
201impl<N: Network> Gateway<N> {
202 #[allow(clippy::too_many_arguments)]
204 pub fn new(
205 account: Account<N>,
206 storage: Storage<N>,
207 ledger: Arc<dyn LedgerService<N>>,
208 ip: Option<SocketAddr>,
209 trusted_validators: &[SocketAddr],
210 trusted_peers_only: bool,
211 node_data_dir: NodeDataDir,
212 dev: Option<u16>,
213 ) -> Result<Self> {
214 let ip = match (ip, dev) {
216 (None, Some(dev)) => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + dev)),
217 (None, None) => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, MEMORY_POOL_PORT)),
218 (Some(ip), _) => ip,
219 };
220 let tcp = Tcp::new(Config::new(ip, Committee::<N>::max_committee_size() * 10));
228
229 let mut initial_peers = HashMap::new();
231
232 if !trusted_peers_only {
234 let cached_peers = Self::load_cached_peers(&node_data_dir.gateway_peer_cache_path())?;
235 for addr in cached_peers {
236 initial_peers.insert(addr, Peer::new_candidate(addr, false));
237 }
238 }
239
240 initial_peers.extend(trusted_validators.iter().copied().map(|addr| (addr, Peer::new_candidate(addr, true))));
243
244 Ok(Self(Arc::new(InnerGateway {
246 account,
247 storage,
248 ledger,
249 tcp,
250 cache: Default::default(),
251 resolver: Default::default(),
252 peer_pool: RwLock::new(initial_peers),
253 #[cfg(feature = "telemetry")]
254 validator_telemetry: Default::default(),
255 primary_sender: Default::default(),
256 worker_senders: Default::default(),
257 sync_sender: Default::default(),
258 handles: Default::default(),
259 node_data_dir,
260 trusted_peers_only,
261 dev,
262 })))
263 }
264
265 pub async fn run(
267 &self,
268 primary_sender: PrimarySender<N>,
269 worker_senders: IndexMap<u8, WorkerSender<N>>,
270 sync_sender: Option<SyncSender<N>>,
271 ) {
272 debug!("Starting the gateway for the memory pool...");
273
274 self.primary_sender.set(primary_sender).expect("Primary sender already set in gateway");
276
277 self.worker_senders.set(worker_senders).expect("The worker senders are already set");
279
280 if let Some(sync_sender) = sync_sender {
282 self.sync_sender.set(sync_sender).expect("Sync sender already set in gateway");
283 }
284
285 self.enable_handshake().await;
287 self.enable_reading().await;
288 self.enable_writing().await;
289 self.enable_disconnect().await;
290 self.enable_on_connect().await;
291
292 #[cfg(feature = "metrics")]
294 {
295 let gateway = self.clone();
296 self.spawn(async move {
297 loop {
298 tokio::time::sleep(Duration::from_secs(1)).await;
299 gateway.update_metrics();
300 }
301 });
302 }
303
304 let listen_addr = self.tcp.enable_listener().await.expect("Failed to enable the TCP listener");
306 debug!("Listening for validator connections at address {listen_addr:?}");
307
308 self.initialize_heartbeat();
310
311 info!("Started the gateway for the memory pool at '{}'", self.local_ip());
312 }
313}
314
315impl<N: Network> Gateway<N> {
317 fn max_committee_size(&self) -> usize {
319 self.ledger
320 .current_committee()
321 .map_or_else(|_e| Committee::<N>::max_committee_size() as usize, |committee| committee.num_members())
322 }
323
324 fn max_cache_events(&self) -> usize {
326 self.max_cache_transmissions()
327 }
328
329 fn max_cache_certificates(&self) -> usize {
331 2 * BatchHeader::<N>::MAX_GC_ROUNDS * self.max_committee_size()
332 }
333
334 fn max_cache_transmissions(&self) -> usize {
336 self.max_cache_certificates() * BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH
337 }
338
339 fn max_cache_duplicates(&self) -> usize {
341 self.max_committee_size().pow(2)
342 }
343}
344
345#[async_trait]
346impl<N: Network> CommunicationService for Gateway<N> {
347 type Message = Event<N>;
349
350 fn prepare_block_request(start_height: u32, end_height: u32) -> Self::Message {
352 debug_assert!(start_height < end_height, "Invalid block request format");
353 Event::BlockRequest(BlockRequest { start_height, end_height })
354 }
355
356 async fn send(&self, peer_ip: SocketAddr, message: Self::Message) -> Option<oneshot::Receiver<io::Result<()>>> {
362 Transport::send(self, peer_ip, message).await
363 }
364}
365
366impl<N: Network> Gateway<N> {
367 pub fn account(&self) -> &Account<N> {
369 &self.account
370 }
371
372 pub fn dev(&self) -> Option<u16> {
374 self.dev
375 }
376
377 pub fn ledger(&self) -> &Arc<dyn LedgerService<N>> {
379 &self.ledger
380 }
381
382 pub fn resolver(&self) -> &RwLock<Resolver<N>> {
384 &self.resolver
385 }
386
387 pub fn resolve_to_listener(&self, connected_addr: &SocketAddr) -> Option<SocketAddr> {
389 self.resolver.read().get_listener(*connected_addr)
390 }
391
392 #[cfg(feature = "telemetry")]
394 pub fn validator_telemetry(&self) -> &Telemetry<N> {
395 &self.validator_telemetry
396 }
397
398 pub fn primary_sender(&self) -> &PrimarySender<N> {
400 self.primary_sender.get().expect("Primary sender not set in gateway")
401 }
402
403 pub fn num_workers(&self) -> u8 {
405 u8::try_from(self.worker_senders.get().expect("Missing worker senders in gateway").len())
406 .expect("Too many workers")
407 }
408
409 pub fn get_worker_sender(&self, worker_id: u8) -> Option<&WorkerSender<N>> {
411 self.worker_senders.get().and_then(|senders| senders.get(&worker_id))
412 }
413
414 pub fn is_authorized_validator_ip(&self, ip: SocketAddr) -> bool {
416 if self.trusted_peers().contains(&ip) {
418 return true;
419 }
420 match self.resolve_to_aleo_addr(ip) {
422 Some(address) => self.is_authorized_validator_address(address),
424 None => {
425 warn!("{CONTEXT} Could not resolve the Aleo address for '{ip}'");
426 false
427 }
428 }
429 }
430
431 pub fn is_authorized_validator_address(&self, validator_address: Address<N>) -> bool {
433 if self
442 .ledger
443 .get_committee_lookback_for_round(self.storage.current_round())
444 .is_ok_and(|committee| committee.is_committee_member(validator_address))
445 {
446 return true;
447 }
448
449 if self.ledger.current_committee().is_ok_and(|committee| committee.is_committee_member(validator_address)) {
451 return true;
452 }
453
454 let previous_block_height = self.ledger.latest_block_height().saturating_sub(MAX_BLOCKS_BEHIND);
456 match self.ledger.get_block_round(previous_block_height) {
458 Ok(block_round) => (block_round..self.storage.current_round()).step_by(2).any(|round| {
459 self.ledger
460 .get_committee_lookback_for_round(round)
461 .is_ok_and(|committee| committee.is_committee_member(validator_address))
462 }),
463 Err(_) => false,
464 }
465 }
466
467 pub fn connected_addresses(&self) -> HashSet<Address<N>> {
469 self.get_connected_peers().into_iter().map(|peer| peer.aleo_addr).collect()
470 }
471
472 fn ensure_peer_is_allowed(&self, listener_addr: SocketAddr) -> Result<(), DisconnectReason> {
474 if self.is_local_ip(listener_addr) {
476 return Err(DisconnectReason::SelfConnect);
477 }
478
479 Ok(())
480 }
481
482 #[cfg(feature = "metrics")]
484 fn update_metrics(&self) {
485 if let Some(count) = self.number_of_connected_validators() {
486 metrics::gauge(metrics::bft::CONNECTED, count as f64);
487 }
488 if let Some(count) = self.number_of_connecting_peers() {
489 metrics::gauge(metrics::bft::CONNECTING, count as f64);
490 }
491 }
492
493 #[cfg(test)]
495 pub fn insert_connected_peer(&self, peer_ip: SocketAddr, peer_addr: SocketAddr, address: Address<N>) {
496 self.resolver.write().insert_peer(peer_ip, peer_addr, Some(address));
498 self.peer_pool.write().insert(peer_ip, Peer::new_connecting(peer_ip, false));
500 if let Some(peer) = self.peer_pool.write().get_mut(&peer_ip) {
501 peer.upgrade_to_connected(
502 peer_addr,
503 peer_ip.port(),
504 address,
505 NodeType::Validator,
506 0,
507 get_repo_commit_hash(),
508 ConnectionMode::Gateway,
509 );
510 }
511 }
512
513 fn send_inner(&self, peer_ip: SocketAddr, event: Event<N>) -> Option<oneshot::Receiver<io::Result<()>>> {
519 let Some(peer_addr) = self.resolve_to_ambiguous(peer_ip) else {
521 warn!("Unable to resolve the listener IP address '{peer_ip}'");
522 return None;
523 };
524 let name = event.name();
526 trace!("{CONTEXT} Sending '{name}' to '{peer_ip}'");
528 let result = self.unicast(peer_addr, event);
529 if let Err(err) = &result {
531 warn!("{CONTEXT} Failed to send '{name}' to '{peer_ip}': {err:?}");
532 debug!("{CONTEXT} Disconnecting from '{peer_ip}' (unable to send)");
533 self.disconnect(peer_ip);
534 }
535 result.ok()
536 }
537
538 async fn inbound(&self, peer_addr: SocketAddr, event: Event<N>) -> Result<bool> {
542 let Some(peer_ip) = self.resolver.read().get_listener(peer_addr) else {
544 trace!("Dropping a {} from {peer_addr} - no longer connected.", event.name());
546 return Ok(false);
547 };
548 if !(self.is_authorized_validator_ip(peer_ip)
550 || self
551 .get_connected_peer(peer_ip)
552 .map(|peer| peer.node_type == NodeType::BootstrapClient)
553 .unwrap_or(false))
554 {
555 bail!("{CONTEXT} Dropping '{}' from '{peer_ip}' (not authorized)", event.name())
556 }
557 let num_events = self.cache.insert_inbound_event(peer_ip, CACHE_EVENTS_INTERVAL);
559 if num_events >= self.max_cache_events() {
560 bail!("Dropping '{peer_ip}' for spamming events (num_events = {num_events})")
561 }
562 match event {
564 Event::CertificateRequest(_) | Event::CertificateResponse(_) => {
565 let certificate_id = match &event {
567 Event::CertificateRequest(CertificateRequest { certificate_id }) => *certificate_id,
568 Event::CertificateResponse(CertificateResponse { certificate }) => certificate.id(),
569 _ => unreachable!(),
570 };
571 let num_events = self.cache.insert_inbound_certificate(certificate_id, CACHE_REQUESTS_INTERVAL);
573 if num_events >= self.max_cache_duplicates() {
574 return Ok(true);
575 }
576 }
577 Event::TransmissionRequest(TransmissionRequest { transmission_id })
578 | Event::TransmissionResponse(TransmissionResponse { transmission_id, .. }) => {
579 let num_events = self.cache.insert_inbound_transmission(transmission_id, CACHE_REQUESTS_INTERVAL);
581 if num_events >= self.max_cache_duplicates() {
582 return Ok(true);
583 }
584 }
585 Event::BlockRequest(_) => {
586 let num_events = self.cache.insert_inbound_block_request(peer_ip, CACHE_REQUESTS_INTERVAL);
587 if num_events >= self.max_cache_duplicates() {
588 return Ok(true);
589 }
590 }
591 _ => {}
592 }
593 trace!("{CONTEXT} Received '{}' from '{peer_ip}'", event.name());
594
595 match event {
598 Event::BatchPropose(batch_propose) => {
599 let _ = self.primary_sender().tx_batch_propose.send((peer_ip, batch_propose)).await;
601 Ok(true)
602 }
603 Event::BatchSignature(batch_signature) => {
604 let _ = self.primary_sender().tx_batch_signature.send((peer_ip, batch_signature)).await;
606 Ok(true)
607 }
608 Event::BatchCertified(batch_certified) => {
609 let _ = self.primary_sender().tx_batch_certified.send((peer_ip, batch_certified.certificate)).await;
611 Ok(true)
612 }
613 Event::BlockRequest(block_request) => {
614 let BlockRequest { start_height, end_height } = block_request;
615
616 if start_height >= end_height {
618 bail!("Block request from '{peer_ip}' has an invalid range ({start_height}..{end_height})")
619 }
620 if end_height - start_height > DataBlocks::<N>::MAXIMUM_NUMBER_OF_BLOCKS as u32 {
622 bail!("Block request from '{peer_ip}' has an excessive range ({start_height}..{end_height})")
623 }
624
625 let latest_consensus_version = N::CONSENSUS_VERSION(end_height - 1)?;
627
628 let self_ = self.clone();
629 let blocks = match task::spawn_blocking(move || {
630 match self_.ledger.get_blocks(start_height..end_height) {
632 Ok(blocks) => Ok(DataBlocks(blocks)),
633 Err(error) => bail!("Missing blocks {start_height} to {end_height} from ledger - {error}"),
634 }
635 })
636 .await
637 {
638 Ok(Ok(blocks)) => blocks,
639 Ok(Err(error)) => return Err(error),
640 Err(error) => return Err(anyhow!("[BlockRequest] {error}")),
641 };
642
643 let self_ = self.clone();
644 tokio::spawn(async move {
645 let event =
647 Event::BlockResponse(BlockResponse::new(block_request, blocks, latest_consensus_version));
648 Transport::send(&self_, peer_ip, event).await;
649 });
650 Ok(true)
651 }
652 Event::BlockResponse(BlockResponse { request, latest_consensus_version, blocks, .. }) => {
653 if let Some(sync_sender) = self.sync_sender.get() {
655 if !self.cache.remove_outbound_block_request(peer_ip, &request) {
657 bail!("Unsolicited block response from '{peer_ip}'")
658 }
659
660 let (send, recv) = tokio::sync::oneshot::channel();
664 rayon::spawn_fifo(move || {
665 let blocks = blocks.deserialize_blocking().map_err(|error| anyhow!("[BlockResponse] {error}"));
666 let _ = send.send(blocks);
667 });
668 let blocks = match recv.await {
669 Ok(Ok(blocks)) => blocks,
670 Ok(Err(error)) => bail!("Peer '{peer_ip}' sent an invalid block response - {error}"),
671 Err(error) => bail!("Peer '{peer_ip}' sent an invalid block response - {error}"),
672 };
673
674 blocks.ensure_response_is_well_formed(peer_ip, request.start_height, request.end_height)?;
676 match sync_sender.insert_block_response(peer_ip, blocks.0, latest_consensus_version).await {
678 Ok(_) => Ok(true),
679 Err(err) if err.is_benign() => {
680 let err: anyhow::Error = err.into();
681 let err = err.context(format!("Ignoring block response from peer '{peer_ip}'"));
682 debug!("{}", flatten_error(err));
683 Ok(true)
684 }
685 Err(err) if err.is_invalid_consensus_version() => {
686 let err: anyhow::Error = err.into();
687 let err = err.context(format!("Peer sent an invalid block response '{peer_ip}'"));
688
689 let msg = flatten_error(&err);
690 error!("{msg}");
691 self.ip_ban_peer(peer_ip, Some(&msg));
692 Err(err)
693 }
694 Err(err) => {
695 let err: anyhow::Error = err.into();
696 let err = err.context(format!("Peer '{peer_ip}' sent an invalid block response"));
697 warn!("{}", flatten_error(err));
698
699 Ok(true)
701 }
702 }
703 } else {
704 debug!("Ignoring block response from '{peer_ip}' - no sync sender");
705 Ok(true)
706 }
707 }
708 Event::CertificateRequest(certificate_request) => {
709 if let Some(sync_sender) = self.sync_sender.get() {
712 let _ = sync_sender.tx_certificate_request.send((peer_ip, certificate_request)).await;
714 }
715 Ok(true)
716 }
717 Event::CertificateResponse(certificate_response) => {
718 if let Some(sync_sender) = self.sync_sender.get() {
721 let _ = sync_sender.tx_certificate_response.send((peer_ip, certificate_response)).await;
723 }
724 Ok(true)
725 }
726 Event::ChallengeRequest(..) | Event::ChallengeResponse(..) => {
727 bail!("{CONTEXT} Peer '{peer_ip}' is not following the protocol")
729 }
730 Event::Disconnect(message) => {
731 debug!("Peer '{peer_ip}' decided to disconnect due to '{}'", message.reason);
733 self.disconnect(peer_ip);
734 Ok(false)
735 }
736 Event::PrimaryPing(ping) => {
737 let PrimaryPing { version, block_locators, primary_certificate } = ping;
738
739 if version < Event::<N>::VERSION {
741 bail!("Dropping '{peer_ip}' on event version {version} (outdated)");
742 }
743
744 debug!("Validator '{peer_ip}' is at height {}", block_locators.latest_locator_height());
746
747 if let Some(sync_sender) = self.sync_sender.get() {
749 if let Err(error) = sync_sender.update_peer_locators(peer_ip, block_locators).await {
751 bail!("Validator '{peer_ip}' sent invalid block locators - {error}");
752 }
753 }
754
755 let _ = self.primary_sender().tx_primary_ping.send((peer_ip, primary_certificate)).await;
757 Ok(true)
758 }
759 Event::TransmissionRequest(request) => {
760 let Ok(worker_id) = assign_to_worker(request.transmission_id, self.num_workers()) else {
763 warn!("{CONTEXT} Unable to assign transmission ID '{}' to a worker", request.transmission_id);
764 return Ok(true);
765 };
766 if let Some(sender) = self.get_worker_sender(worker_id) {
768 let _ = sender.tx_transmission_request.send((peer_ip, request)).await;
770 }
771 Ok(true)
772 }
773 Event::TransmissionResponse(response) => {
774 let Ok(worker_id) = assign_to_worker(response.transmission_id, self.num_workers()) else {
776 warn!("{CONTEXT} Unable to assign transmission ID '{}' to a worker", response.transmission_id);
777 return Ok(true);
778 };
779 if let Some(sender) = self.get_worker_sender(worker_id) {
781 let _ = sender.tx_transmission_response.send((peer_ip, response)).await;
783 }
784 Ok(true)
785 }
786 Event::ValidatorsRequest(_) => {
787 let mut connected_peers = self.get_best_connected_peers(Some(MAX_VALIDATORS_TO_SEND));
788 connected_peers.shuffle(&mut rand::rng());
789
790 let self_ = self.clone();
791 tokio::spawn(async move {
792 let mut validators = IndexMap::with_capacity(MAX_VALIDATORS_TO_SEND);
794 for validator in connected_peers.into_iter() {
796 validators.insert(validator.listener_addr, validator.aleo_addr);
798 }
799 let event = Event::ValidatorsResponse(ValidatorsResponse { validators });
801 Transport::send(&self_, peer_ip, event).await;
802 });
803 Ok(true)
804 }
805 Event::ValidatorsResponse(response) => {
806 if self.trusted_peers_only {
807 bail!("{CONTEXT} Not accepting validators response from '{peer_ip}' (trusted peers only)");
808 }
809 let ValidatorsResponse { validators } = response;
810 ensure!(validators.len() <= MAX_VALIDATORS_TO_SEND, "{CONTEXT} Received too many validators");
812 if !self.cache.contains_outbound_validators_request(peer_ip) {
814 bail!("{CONTEXT} Received validators response from '{peer_ip}' without a validators request")
815 }
816 self.cache.decrement_outbound_validators_requests(peer_ip);
818
819 let valid_addrs = validators
822 .into_iter()
823 .filter_map(|(listener_addr, aleo_addr)| {
824 (self.account.address() != aleo_addr
825 && !self.is_connected_address(aleo_addr)
826 && self.is_authorized_validator_address(aleo_addr))
827 .then_some((listener_addr, None))
828 })
829 .collect::<Vec<_>>();
830 if !valid_addrs.is_empty() {
831 self.insert_candidate_peers(valid_addrs);
832 }
833
834 Ok(true)
835 }
836 Event::WorkerPing(ping) => {
837 ensure!(
839 ping.transmission_ids.len() <= Worker::<N>::MAX_TRANSMISSIONS_PER_WORKER_PING,
840 "{CONTEXT} Received too many transmissions"
841 );
842 let num_workers = self.num_workers();
844 for transmission_id in ping.transmission_ids.into_iter() {
846 let Ok(worker_id) = assign_to_worker(transmission_id, num_workers) else {
848 warn!("{CONTEXT} Unable to assign transmission ID '{transmission_id}' to a worker");
849 continue;
850 };
851 if let Some(sender) = self.get_worker_sender(worker_id) {
853 let _ = sender.tx_worker_ping.send((peer_ip, transmission_id)).await;
855 }
856 }
857 Ok(true)
858 }
859 }
860 }
861
862 fn initialize_heartbeat(&self) {
864 let self_clone = self.clone();
865 self.spawn(async move {
866 tokio::time::sleep(Duration::from_millis(1000)).await;
868 info!("Starting the heartbeat of the gateway...");
869 loop {
870 self_clone.heartbeat().await;
872 tokio::time::sleep(Duration::from_secs(15)).await;
874 }
875 });
876 }
877
878 #[allow(dead_code)]
880 fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T) {
881 self.handles.lock().push(tokio::spawn(future));
882 }
883
884 pub async fn shut_down(&self) {
886 info!("Shutting down the gateway...");
887 if let Err(e) = self.save_best_peers(&self.node_data_dir.gateway_peer_cache_path(), None, true) {
889 warn!("Failed to persist best validators to disk: {e}");
890 }
891 self.handles.lock().iter().for_each(|handle| handle.abort());
893 self.tcp.shut_down().await;
895 }
896}
897
898impl<N: Network> Gateway<N> {
899 const MINIMUM_TIME_BETWEEN_CONNECTION_ATTEMPTS: Duration = Duration::from_secs(10);
901 const MISSING_VALIDATOR_CONNECTIONS_GRACE_PERIOD: Duration = Duration::from_secs(60);
903
904 async fn heartbeat(&self) {
906 self.log_connected_validators();
908 #[cfg(feature = "telemetry")]
910 self.log_participation_scores();
911 self.handle_trusted_validators();
913 self.handle_bootstrap_peers().await;
915 self.handle_unauthorized_validators();
917 self.handle_min_connected_validators().await;
919 self.handle_banned_ips();
921 }
922
923 fn log_connected_validators(&self) {
925 let connected_validators = self.filter_connected_peers(|peer| peer.node_type == NodeType::Validator);
928
929 let committee = match self.ledger.current_committee() {
930 Ok(c) => c,
931 Err(err) => {
932 error!("Failed to get current committee: {err}");
933 return;
934 }
935 };
936
937 let validators_total = committee.num_members().saturating_sub(1);
939 let total_validators = format!("(of {validators_total} bonded validators)").dimmed();
941 let connections_msg = match connected_validators.len() {
943 0 => "No connected validators".to_string(),
944 num_connected => format!("Connected to {num_connected} validators {total_validators}"),
945 };
946 info!("{connections_msg}");
947
948 let mut connected_validator_addresses = HashSet::with_capacity(connected_validators.len());
950 let mut connected_validator_shas: HashMap<SmolStr, u64> = HashMap::with_capacity(connected_validators.len());
951 let our_sha = shorten_snarkos_sha(&get_repo_commit_hash());
953 let our_stake = committee.get_stake(self.account.address());
954 connected_validator_shas.insert(our_sha.clone(), our_stake);
955 connected_validator_addresses.insert(self.account.address());
957 for peer in &connected_validators {
959 let address = peer.aleo_addr;
961 connected_validator_addresses.insert(address);
962 let address_stake = committee.get_stake(address);
964 let short_peer_sha = shorten_snarkos_sha(&peer.snarkos_sha);
965 *connected_validator_shas.entry(short_peer_sha.clone()).or_default() += address_stake;
966
967 debug!(
968 "{}",
969 format!(
970 " Connected to: {} - {} (connection age {:?})",
971 peer.listener_addr,
972 peer.aleo_addr,
973 peer.first_seen.elapsed()
974 )
975 .dimmed()
976 );
977 }
978
979 if let Some(combined_stake) = connected_validator_shas.get(&our_sha) {
981 let percentage = *combined_stake as f64 / committee.total_stake() as f64 * 100.0;
982 debug!("{}", format!(" Combined stake @ {our_sha}: {percentage:.2}%").dimmed());
983 #[cfg(feature = "metrics")]
984 metrics::gauge(metrics::bft::CONNECTED_STAKE_WITH_MATCHING_SHA, percentage);
985 }
986
987 let num_not_connected = validators_total.saturating_sub(connected_validators.len());
989 if num_not_connected > 0 && self.tcp().uptime() > Self::MISSING_VALIDATOR_CONNECTIONS_GRACE_PERIOD {
990 let total_stake = committee.total_stake();
992 let total_stake_f64 = total_stake as f64;
993
994 let committee_members: HashSet<_> =
996 self.ledger.current_committee().map(|c| c.members().keys().copied().collect()).unwrap_or_default();
997
998 let not_connected_stake: u64 = committee_members
999 .difference(&connected_validator_addresses)
1000 .map(|address| {
1001 let address_stake = committee.get_stake(*address);
1002 let address_stake_as_percentage =
1003 if total_stake == 0 { 0.0 } else { address_stake as f64 / total_stake_f64 * 100.0 };
1004 debug!(
1005 "{}",
1006 format!(" Not connected to {address} ({address_stake_as_percentage:.2}% of total stake)")
1007 .dimmed()
1008 );
1009 address_stake
1010 })
1011 .sum();
1012
1013 let not_connected_stake_as_percentage =
1014 if total_stake == 0 { 0.0 } else { not_connected_stake as f64 / total_stake_f64 * 100.0 };
1015 warn!(
1016 "Not connected to {num_not_connected} validators {total_validators} ({not_connected_stake_as_percentage:.2}% of total stake not connected)"
1017 );
1018 #[cfg(feature = "metrics")]
1019 {
1020 let connected_stake_as_percentage = 100.0 - not_connected_stake_as_percentage;
1021 metrics::gauge(metrics::bft::CONNECTED_STAKE, connected_stake_as_percentage);
1022 }
1023 } else {
1024 #[cfg(feature = "metrics")]
1025 metrics::gauge(metrics::bft::CONNECTED_STAKE, 100.0);
1026 };
1027
1028 if !committee.is_quorum_threshold_reached(&connected_validator_addresses) {
1029 if self.tcp().uptime() > Self::MISSING_VALIDATOR_CONNECTIONS_GRACE_PERIOD {
1031 error!("Not connected to a quorum of validators");
1032 } else {
1033 debug!("Not connected to a quorum of validators");
1034 }
1035 }
1036 }
1037
1038 #[cfg(feature = "telemetry")]
1040 fn log_participation_scores(&self) {
1041 if let Ok(committee_lookback) = self.ledger.get_committee_lookback_for_round(self.storage.current_round()) {
1042 let participation_scores = self.validator_telemetry().get_participation_scores(&committee_lookback);
1044
1045 debug!("Participation Scores (in the last {} rounds):", self.storage.max_gc_rounds());
1047 for (address, (cert_score, sig_score)) in participation_scores {
1048 debug!(
1049 "{}",
1050 format!(" {address} - certificates: {cert_score:.2}% signatures: {sig_score:.2}%").dimmed()
1051 );
1052 }
1053 }
1054 }
1055
1056 fn handle_trusted_validators(&self) {
1058 let trusted_peers = self.trusted_peers();
1059
1060 let handles: Vec<JoinHandle<_>> = trusted_peers
1062 .iter()
1063 .filter_map(|validator_ip| {
1064 match self.connect(*validator_ip) {
1066 Ok(hdl) => Some(hdl),
1067 Err(ConnectError::SelfConnect { .. })
1068 | Err(ConnectError::AlreadyConnected { .. })
1069 | Err(ConnectError::AlreadyConnecting { .. }) => None,
1070 Err(err) => {
1071 warn!("Could not initiate connection to trusted validator at '{validator_ip}' - {err}");
1072 None
1073 }
1074 }
1075 })
1076 .collect();
1077
1078 if !handles.is_empty() {
1079 info!("Reconnnecting to {} out of {} trusted validators", handles.len(), trusted_peers.len());
1080 }
1081 }
1082
1083 async fn handle_bootstrap_peers(&self) {
1085 if self.trusted_peers_only {
1087 return;
1088 }
1089 let mut candidate_bootstrap = Vec::new();
1091 let connected_bootstrap = self.filter_connected_peers(|peer| peer.node_type == NodeType::BootstrapClient);
1092 for bootstrap_ip in bootstrap_peers::<N>(self.is_dev()) {
1093 if !connected_bootstrap.iter().any(|peer| peer.listener_addr == bootstrap_ip) {
1094 candidate_bootstrap.push(bootstrap_ip);
1095 }
1096 }
1097 if connected_bootstrap.is_empty() {
1099 let peer_to_connect = candidate_bootstrap.into_iter().choose(&mut rand::rng());
1101 if let Some(peer_ip) = peer_to_connect {
1102 match self.connect(peer_ip) {
1103 Ok(hdl) => {
1104 debug!("{CONTEXT} (Re-)connecting to bootstrap peer at '{peer_ip}'");
1105 let result = hdl.await;
1106 if let Err(err) = result {
1107 warn!("{CONTEXT} Failed to connect to bootstrap peer at '{peer_ip}' - {err}");
1108 }
1109 }
1110 Err(ConnectError::AlreadyConnected { .. }) | Err(ConnectError::AlreadyConnecting { .. }) => {}
1111 Err(err) => {
1112 warn!("{CONTEXT} Could not initiate connection to bootstrap peer at '{peer_ip}' - {err}")
1113 }
1114 }
1115 }
1116 }
1117 let num_surplus = connected_bootstrap.len().saturating_sub(1);
1119 if num_surplus > 0 {
1120 let peers_to_disconnect = connected_bootstrap.into_iter().sample(&mut rand::rng(), num_surplus);
1122 for peer in peers_to_disconnect {
1123 info!("{CONTEXT} Disconnecting from '{}' (exceeded maximum bootstrap)", peer.listener_addr);
1124 <Self as Transport<N>>::send(
1125 self,
1126 peer.listener_addr,
1127 Event::Disconnect(DisconnectReason::NoReasonGiven.into()),
1128 )
1129 .await;
1130 self.disconnect(peer.listener_addr);
1132 }
1133 }
1134 }
1135
1136 fn handle_unauthorized_validators(&self) {
1138 let self_ = self.clone();
1139 tokio::spawn(async move {
1140 let validators = self_.get_connected_peers();
1142 for peer in validators {
1144 if peer.node_type == NodeType::BootstrapClient {
1146 continue;
1147 }
1148 if !self_.is_authorized_validator_ip(peer.listener_addr) {
1150 warn!(
1151 "{CONTEXT} Disconnecting from '{}' - Validator is not in the current committee",
1152 peer.listener_addr
1153 );
1154 Transport::send(&self_, peer.listener_addr, DisconnectReason::ProtocolViolation.into()).await;
1155 self_.disconnect(peer.listener_addr);
1157 }
1158 }
1159 });
1160 }
1161
1162 async fn handle_min_connected_validators(&self) {
1166 let trusted_validators = self.trusted_peers();
1169 if self.number_of_connected_peers() < N::LATEST_MAX_CERTIFICATES() as usize {
1170 let (addrs, handles): (Vec<_>, Vec<_>) = self
1171 .get_candidate_peers()
1172 .iter()
1173 .filter_map(|peer| {
1174 if trusted_validators.contains(&peer.listener_addr) {
1175 return None;
1176 }
1177
1178 if let Some(previous_attempt) = peer.last_connection_attempt
1179 && previous_attempt.elapsed() < Self::MINIMUM_TIME_BETWEEN_CONNECTION_ATTEMPTS
1180 {
1181 return None;
1182 }
1183
1184 match self.connect(peer.listener_addr) {
1185 Ok(hdl) => Some((peer.listener_addr, hdl)),
1186 Err(ConnectError::AlreadyConnected { .. })
1187 | Err(ConnectError::AlreadyConnecting { .. })
1188 | Err(ConnectError::SelfConnect { .. }) => None,
1189 Err(err) => {
1190 warn!(
1191 "{CONTEXT} Could not initiate connection to validator at '{}' - {err}",
1192 peer.listener_addr
1193 );
1194 None
1195 }
1196 }
1197 })
1198 .unzip();
1199
1200 for (addr, result) in addrs.into_iter().zip(join_all(handles).await) {
1201 if let Err(err) = result {
1202 warn!("{CONTEXT} Failed to connect to validator at '{addr}' - {err}");
1203 }
1204 }
1205
1206 let validators = self.connected_peers();
1208 if validators.is_empty() {
1210 return;
1211 }
1212 if let Some(validator_ip) = validators.into_iter().choose(&mut rand::rng()) {
1214 let self_ = self.clone();
1215 tokio::spawn(async move {
1216 self_.cache.increment_outbound_validators_requests(validator_ip);
1218 let _ = Transport::send(&self_, validator_ip, Event::ValidatorsRequest(ValidatorsRequest)).await;
1220 });
1221 }
1222 }
1223 }
1224
1225 async fn process_message_inner(&self, peer_addr: SocketAddr, message: Event<N>) {
1227 if let Err(error) = self.inbound(peer_addr, message).await
1229 && let Some(peer_ip) = self.resolver.read().get_listener(peer_addr)
1230 {
1231 warn!("{CONTEXT} Disconnecting from '{peer_ip}' - {error}");
1232 let self_ = self.clone();
1233 tokio::spawn(async move {
1234 Transport::send(&self_, peer_ip, DisconnectReason::ProtocolViolation.into()).await;
1235 self_.disconnect(peer_ip);
1237 });
1238 }
1239 }
1240
1241 fn handle_banned_ips(&self) {
1243 self.tcp.banned_peers().remove_old_bans(IP_BAN_TIME_IN_SECS);
1244 }
1245}
1246
1247#[async_trait]
1248impl<N: Network> Transport<N> for Gateway<N> {
1249 async fn send(&self, peer_ip: SocketAddr, event: Event<N>) -> Option<oneshot::Receiver<io::Result<()>>> {
1257 macro_rules! send {
1258 ($self:ident, $cache_map:ident, $interval:expr, $freq:ident) => {{
1259 while $self.cache.$cache_map(peer_ip, $interval) > $self.$freq() {
1261 tokio::time::sleep(Duration::from_millis(10)).await;
1263 }
1264 $self.send_inner(peer_ip, event)
1266 }};
1267 }
1268
1269 match event {
1271 Event::CertificateRequest(_) | Event::CertificateResponse(_) => {
1272 self.cache.insert_outbound_event(peer_ip, CACHE_EVENTS_INTERVAL);
1274 send!(self, insert_outbound_certificate, CACHE_REQUESTS_INTERVAL, max_cache_certificates)
1276 }
1277 Event::TransmissionRequest(_) | Event::TransmissionResponse(_) => {
1278 self.cache.insert_outbound_event(peer_ip, CACHE_EVENTS_INTERVAL);
1280 send!(self, insert_outbound_transmission, CACHE_REQUESTS_INTERVAL, max_cache_transmissions)
1282 }
1283 Event::BlockRequest(request) => {
1284 self.cache.insert_outbound_block_request(peer_ip, request);
1286 send!(self, insert_outbound_event, CACHE_EVENTS_INTERVAL, max_cache_events)
1288 }
1289 _ => {
1290 send!(self, insert_outbound_event, CACHE_EVENTS_INTERVAL, max_cache_events)
1292 }
1293 }
1294 }
1295
1296 fn broadcast(&self, event: Event<N>) {
1300 if self.number_of_connected_peers() > 0 {
1302 let self_ = self.clone();
1303 let connected_peers = self.connected_peers();
1304 tokio::spawn(async move {
1305 for peer_ip in connected_peers {
1307 let _ = Transport::send(&self_, peer_ip, event.clone()).await;
1309 }
1310 });
1311 }
1312 }
1313}
1314
1315impl<N: Network> P2P for Gateway<N> {
1316 fn tcp(&self) -> &Tcp {
1318 &self.tcp
1319 }
1320}
1321
1322#[async_trait]
1323impl<N: Network> Reading for Gateway<N> {
1324 type Codec = EventCodec<N>;
1325 type Message = Event<N>;
1326
1327 fn codec(&self, _peer_addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
1330 Default::default()
1331 }
1332
1333 async fn process_message(&self, peer_addr: SocketAddr, message: Self::Message) -> io::Result<()> {
1335 if matches!(message, Event::BlockRequest(_) | Event::BlockResponse(_)) {
1336 let self_ = self.clone();
1337 tokio::spawn(async move {
1340 self_.process_message_inner(peer_addr, message).await;
1341 });
1342 } else {
1343 self.process_message_inner(peer_addr, message).await;
1344 }
1345 Ok(())
1346 }
1347
1348 fn message_queue_depth(&self) -> usize {
1351 2 * BatchHeader::<N>::MAX_GC_ROUNDS
1352 * N::LATEST_MAX_CERTIFICATES() as usize
1353 * BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH
1354 }
1355}
1356
1357#[async_trait]
1358impl<N: Network> Writing for Gateway<N> {
1359 type Codec = EventCodec<N>;
1360 type Message = Event<N>;
1361
1362 fn codec(&self, _peer_addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
1365 Default::default()
1366 }
1367
1368 fn message_queue_depth(&self) -> usize {
1372 2 * BatchHeader::<N>::MAX_GC_ROUNDS
1373 * N::LATEST_MAX_CERTIFICATES() as usize
1374 * BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH
1375 }
1376}
1377
1378#[async_trait]
1379impl<N: Network> Disconnect for Gateway<N> {
1380 async fn handle_disconnect(&self, peer_addr: SocketAddr, origin: DisconnectOrigin) {
1382 debug!("Physically disconnecting from {peer_addr}; origin: {origin:?}");
1383
1384 if let Some(peer_ip) = self.resolve_to_listener(&peer_addr) {
1385 let was_fully_connected = self.downgrade_peer_to_candidate(peer_ip);
1391
1392 if was_fully_connected && let Some(sync_sender) = self.sync_sender.get() {
1394 let (tx, rx) = oneshot::channel();
1395
1396 if let Err(err) = sync_sender.tx_block_sync_remove_peer.send((peer_ip, tx)).await {
1397 let err: anyhow::Error = err.into();
1398 let err =
1399 err.context(format!("Unable to remove disconnecting peer '{peer_ip}' from the sync module"));
1400 warn!("{CONTEXT} {}", flatten_error(err));
1401 }
1402
1403 if let Err(err) = rx.await {
1404 let err: anyhow::Error = err.into();
1405 let err =
1406 err.context(format!("Unable to remove disconnecting peer '{peer_ip}' from the sync module"));
1407 warn!("{CONTEXT} {}", flatten_error(err));
1408 }
1409 }
1410 self.cache.clear_outbound_validators_requests(peer_ip);
1414 self.cache.clear_outbound_block_requests(peer_ip);
1415 } else {
1416 warn!("{CONTEXT} Got disconnect for a peer '{peer_addr}' that is not in the peer pool");
1417 }
1418 }
1419}
1420
1421#[async_trait]
1422impl<N: Network> OnConnect for Gateway<N> {
1423 async fn on_connect(&self, peer_addr: SocketAddr) {
1424 if let Some(listener_addr) = self.resolve_to_listener(&peer_addr) {
1425 if let Some(peer) = self.get_connected_peer(listener_addr) {
1426 if peer.node_type == NodeType::BootstrapClient {
1427 self.cache.increment_outbound_validators_requests(listener_addr);
1428 let _ =
1429 <Self as Transport<N>>::send(self, listener_addr, Event::ValidatorsRequest(ValidatorsRequest))
1430 .await;
1431 }
1432 }
1433 }
1434 }
1435}
1436
1437#[async_trait]
1438impl<N: Network> Handshake for Gateway<N> {
1439 async fn perform_handshake(&self, mut connection: Connection) -> Result<Connection, ConnectError> {
1441 let peer_addr = connection.addr();
1443 let peer_side = connection.side();
1444
1445 #[cfg(not(test))]
1447 if self.dev().is_none() && peer_side == ConnectionSide::Initiator {
1448 if self.is_ip_banned(peer_addr.ip()) {
1450 trace!("{CONTEXT} Rejected a connection request from banned IP '{}'", peer_addr.ip());
1451 return Err(ConnectError::BannedIp { ip: peer_addr.ip() });
1452 }
1453
1454 let num_attempts = self.cache.insert_inbound_connection(peer_addr.ip(), CONNECTION_ATTEMPTS_SINCE_SECS);
1455
1456 debug!("Number of connection attempts from '{}': {}", peer_addr.ip(), num_attempts);
1457 if num_attempts > MAX_CONNECTION_ATTEMPTS {
1458 self.update_ip_ban(peer_addr.ip());
1459 trace!("{CONTEXT} Rejected a consecutive connection request from IP '{}'", peer_addr.ip());
1460 return Err(ConnectError::other(anyhow!("'{}' appears to be spamming connections", peer_addr.ip())));
1461 }
1462 }
1463
1464 let stream = self.borrow_stream(&mut connection);
1465
1466 let mut listener_addr = if peer_side == ConnectionSide::Initiator {
1469 debug!("{CONTEXT} Received a connection request from '{peer_addr}'");
1470 None
1471 } else {
1472 debug!("{CONTEXT} Shaking hands with {peer_addr}...");
1473 Some(peer_addr)
1474 };
1475
1476 let restrictions_id = self.ledger.latest_restrictions_id();
1478
1479 let handshake_result = if peer_side == ConnectionSide::Responder {
1481 self.handshake_inner_initiator(peer_addr, restrictions_id, stream).await
1482 } else {
1483 self.handshake_inner_responder(peer_addr, &mut listener_addr, restrictions_id, stream).await
1484 };
1485
1486 if let Some(addr) = listener_addr {
1487 match handshake_result {
1488 Ok(ref cr) => {
1489 let node_type = if bootstrap_peers::<N>(self.is_dev()).contains(&addr) {
1490 NodeType::BootstrapClient
1491 } else {
1492 NodeType::Validator
1493 };
1494
1495 if let Some(peer) = self.peer_pool.write().get_mut(&addr) {
1496 self.resolver.write().insert_peer(addr, peer_addr, Some(cr.address));
1497 peer.upgrade_to_connected(
1498 peer_addr,
1499 cr.listener_port,
1500 cr.address,
1501 node_type,
1502 cr.version,
1503 cr.snarkos_sha,
1504 ConnectionMode::Gateway,
1505 );
1506 }
1507 info!("{CONTEXT} Connected to '{addr}'");
1508 }
1509 Err(error) => {
1510 if let Some(peer) = self.peer_pool.write().get_mut(&addr) {
1511 if peer.is_connecting() {
1513 peer.downgrade_to_candidate(addr);
1514 }
1515 }
1516 return Err(error);
1517 }
1518 }
1519 }
1520
1521 Ok(connection)
1522 }
1523}
1524
1525macro_rules! expect_event {
1527 ($event_ty:path, $framed:expr, $peer_addr:expr) => {
1528 match $framed.try_next().await? {
1529 Some($event_ty(data)) => {
1531 trace!("{CONTEXT} Received '{}' from '{}'", data.name(), $peer_addr);
1532 data
1533 }
1534 Some(Event::Disconnect($crate::events::Disconnect { reason })) => {
1536 return Err(ConnectError::other(format!("'{}' disconnected with reason \"{reason}\"", $peer_addr)));
1537 }
1538 Some(ty) => {
1540 return Err(ConnectError::other(format!(
1541 "'{}' did not follow the handshake protocol: received {:?} instead of {}",
1542 $peer_addr,
1543 ty.name(),
1544 stringify!($msg_ty),
1545 )));
1546 }
1547 None => return Err(ConnectError::IoError(io::ErrorKind::BrokenPipe.into())),
1549 }
1550 };
1551}
1552
1553async fn send_event<N: Network>(
1555 framed: &mut Framed<&mut TcpStream, EventCodec<N>>,
1556 peer_addr: SocketAddr,
1557 event: Event<N>,
1558) -> io::Result<()> {
1559 trace!("{CONTEXT} Sending '{}' to '{peer_addr}'", event.name());
1560 framed.send(event).await
1561}
1562
1563impl<N: Network> Gateway<N> {
1564 async fn handshake_inner_initiator<'a>(
1566 &'a self,
1567 peer_addr: SocketAddr,
1568 restrictions_id: Field<N>,
1569 stream: &'a mut TcpStream,
1570 ) -> Result<ChallengeRequest<N>, ConnectError> {
1571 self.add_connecting_peer(peer_addr)?;
1573
1574 let mut framed = Framed::new(stream, EventCodec::<N>::handshake());
1576
1577 let our_nonce: u64 = rand::random();
1581 let current_block_height = self.ledger.latest_block_height();
1583 let consensus_version = N::CONSENSUS_VERSION(current_block_height).unwrap();
1584 let snarkos_sha = match (self.is_dev(), consensus_version >= ConsensusVersion::V12, get_repo_commit_hash()) {
1585 (true, _, Some(sha)) => Some(sha),
1586 (_, true, Some(sha)) => Some(sha),
1587 _ => None,
1588 };
1589 let our_request = ChallengeRequest::new(self.local_ip().port(), self.account.address(), our_nonce, snarkos_sha);
1591 send_event(&mut framed, peer_addr, Event::ChallengeRequest(our_request)).await?;
1592
1593 let peer_response = expect_event!(Event::ChallengeResponse, framed, peer_addr);
1597 let peer_request = expect_event!(Event::ChallengeRequest, framed, peer_addr);
1599
1600 if let Some(reason) = self
1602 .verify_challenge_response(peer_addr, peer_request.address, peer_response, restrictions_id, our_nonce)
1603 .await
1604 {
1605 send_event(&mut framed, peer_addr, reason.into()).await?;
1606 return Err(ConnectError::application(reason));
1607 }
1608
1609 if let Some(reason) = self.verify_challenge_request(peer_addr, &peer_request) {
1611 send_event(&mut framed, peer_addr, reason.into()).await?;
1612 return Err(reason.into_connect_error(peer_addr));
1613 }
1614
1615 let response_nonce: u64 = rand::random();
1619 let data = [peer_request.nonce.to_le_bytes(), response_nonce.to_le_bytes()].concat();
1620 let Ok(our_signature) = self.account.sign_bytes(&data, &mut rand::rng()) else {
1621 return Err(ConnectError::other(anyhow!("Failed to sign the challenge request nonce")));
1622 };
1623 let our_response =
1625 ChallengeResponse { restrictions_id, signature: Data::Object(our_signature), nonce: response_nonce };
1626 send_event(&mut framed, peer_addr, Event::ChallengeResponse(our_response)).await?;
1627
1628 Ok(peer_request)
1629 }
1630
1631 async fn handshake_inner_responder<'a>(
1633 &'a self,
1634 peer_addr: SocketAddr,
1635 peer_ip: &mut Option<SocketAddr>,
1636 restrictions_id: Field<N>,
1637 stream: &'a mut TcpStream,
1638 ) -> Result<ChallengeRequest<N>, ConnectError> {
1639 let mut framed = Framed::new(stream, EventCodec::<N>::handshake());
1641
1642 let peer_request = expect_event!(Event::ChallengeRequest, framed, peer_addr);
1646
1647 if self.account.address() == peer_request.address {
1649 return Err(ConnectError::SelfConnect { address: peer_addr });
1650 }
1651
1652 *peer_ip = Some(SocketAddr::new(peer_addr.ip(), peer_request.listener_port));
1654 let peer_ip = peer_ip.unwrap();
1655
1656 if let Err(reason) = self.ensure_peer_is_allowed(peer_ip) {
1658 send_event(&mut framed, peer_addr, reason.into()).await?;
1659 return Err(reason.into_connect_error(peer_addr));
1660 }
1661
1662 self.add_connecting_peer(peer_ip)?;
1664
1665 if let Some(reason) = self.verify_challenge_request(peer_addr, &peer_request) {
1667 send_event(&mut framed, peer_addr, reason.into()).await?;
1668 return Err(reason.into_connect_error(peer_addr));
1669 }
1670
1671 let response_nonce: u64 = rand::random();
1675 let data = [peer_request.nonce.to_le_bytes(), response_nonce.to_le_bytes()].concat();
1676 let Ok(our_signature) = self.account.sign_bytes(&data, &mut rand::rng()) else {
1677 return Err(ConnectError::other(anyhow!("Failed to sign the challenge request nonce")));
1678 };
1679 let our_response =
1681 ChallengeResponse { restrictions_id, signature: Data::Object(our_signature), nonce: response_nonce };
1682 send_event(&mut framed, peer_addr, Event::ChallengeResponse(our_response)).await?;
1683
1684 let our_nonce: u64 = rand::random();
1686 let current_block_height = self.ledger.latest_block_height();
1688 let consensus_version = N::CONSENSUS_VERSION(current_block_height).unwrap();
1689 let snarkos_sha = match (self.is_dev(), consensus_version >= ConsensusVersion::V12, get_repo_commit_hash()) {
1690 (true, _, Some(sha)) => Some(sha),
1691 (_, true, Some(sha)) => Some(sha),
1692 _ => None,
1693 };
1694 let our_request = ChallengeRequest::new(self.local_ip().port(), self.account.address(), our_nonce, snarkos_sha);
1696 send_event(&mut framed, peer_addr, Event::ChallengeRequest(our_request)).await?;
1697
1698 let peer_response = expect_event!(Event::ChallengeResponse, framed, peer_addr);
1702 if let Some(reason) = self
1704 .verify_challenge_response(peer_addr, peer_request.address, peer_response, restrictions_id, our_nonce)
1705 .await
1706 {
1707 send_event(&mut framed, peer_addr, reason.into()).await?;
1708 Err(reason.into_connect_error(peer_addr))
1709 } else {
1710 Ok(peer_request)
1711 }
1712 }
1713
1714 #[must_use]
1716 fn verify_challenge_request(&self, peer_addr: SocketAddr, event: &ChallengeRequest<N>) -> Option<DisconnectReason> {
1717 let &ChallengeRequest { version, listener_port, address, nonce: _, ref snarkos_sha } = event;
1719 log_repo_sha_comparison(peer_addr, snarkos_sha, CONTEXT);
1720
1721 let listener_addr = SocketAddr::new(peer_addr.ip(), listener_port);
1722
1723 if version < Event::<N>::VERSION {
1725 return Some(DisconnectReason::OutdatedClientVersion);
1726 }
1727 if self.trusted_peers_only && !self.is_trusted(listener_addr) {
1729 warn!("{CONTEXT} Dropping '{peer_addr}' for being an untrusted validator ({address})");
1730 return Some(DisconnectReason::NoExternalPeersAllowed);
1731 }
1732 if !bootstrap_peers::<N>(self.dev().is_some()).contains(&listener_addr) {
1733 if !self.is_authorized_validator_address(address) {
1735 return Some(DisconnectReason::UnauthorizedValidator);
1736 }
1737 }
1738
1739 if self.is_connected_address(address) {
1741 return Some(DisconnectReason::AlreadyConnectedToAleoAddress);
1742 }
1743
1744 None
1745 }
1746
1747 #[must_use]
1749 async fn verify_challenge_response(
1750 &self,
1751 peer_addr: SocketAddr,
1752 peer_address: Address<N>,
1753 response: ChallengeResponse<N>,
1754 expected_restrictions_id: Field<N>,
1755 expected_nonce: u64,
1756 ) -> Option<DisconnectReason> {
1757 let ChallengeResponse { restrictions_id, signature, nonce } = response;
1759
1760 if restrictions_id != expected_restrictions_id {
1762 warn!("{CONTEXT} Handshake with '{peer_addr}' failed (incorrect restrictions ID)");
1763 return Some(DisconnectReason::InvalidChallengeResponse);
1764 }
1765 let Ok(signature) = spawn_blocking!(signature.deserialize_blocking()) else {
1767 warn!("{CONTEXT} Handshake with '{peer_addr}' failed (cannot deserialize the signature)");
1768 return Some(DisconnectReason::InvalidChallengeResponse);
1769 };
1770 if !signature.verify_bytes(&peer_address, &[expected_nonce.to_le_bytes(), nonce.to_le_bytes()].concat()) {
1772 warn!("{CONTEXT} Handshake with '{peer_addr}' failed (invalid signature)");
1773 return Some(DisconnectReason::InvalidChallengeResponse);
1774 }
1775 None
1776 }
1777}
1778
1779#[cfg(test)]
1780mod prop_tests {
1781 use crate::{
1782 Gateway,
1783 MAX_WORKERS,
1784 MEMORY_POOL_PORT,
1785 Worker,
1786 helpers::{Storage, init_primary_channels, init_worker_channels},
1787 };
1788
1789 use snarkos_account::Account;
1790 use snarkos_node_bft_ledger_service::MockLedgerService;
1791 use snarkos_node_bft_storage_service::BFTMemoryService;
1792 use snarkos_node_network::PeerPoolHandling;
1793 use snarkos_node_tcp::P2P;
1794 use snarkos_utilities::NodeDataDir;
1795
1796 use snarkos_node_bft_events::committee_prop_tests::{CommitteeContext, ValidatorSet};
1797 use snarkvm::{
1798 ledger::{
1799 committee::{Committee, test_helpers::sample_committee_for_round_and_members},
1800 narwhal::{BatchHeader, batch_certificate::test_helpers::sample_batch_certificate_for_round},
1801 },
1802 prelude::{MainnetV0, PrivateKey},
1803 utilities::TestRng,
1804 };
1805
1806 use indexmap::{IndexMap, IndexSet};
1807 use proptest::{
1808 prelude::{Arbitrary, BoxedStrategy, Just, Strategy, any, any_with},
1809 sample::Selector,
1810 };
1811 use std::{
1812 fmt::{Debug, Formatter},
1813 net::{IpAddr, Ipv4Addr, SocketAddr},
1814 sync::Arc,
1815 };
1816 use test_strategy::proptest;
1817
1818 type CurrentNetwork = MainnetV0;
1819
1820 impl Debug for Gateway<CurrentNetwork> {
1821 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1822 f.debug_tuple("Gateway").field(&self.account.address()).field(&self.tcp.config()).finish()
1824 }
1825 }
1826
1827 #[derive(Debug, test_strategy::Arbitrary)]
1828 enum GatewayAddress {
1829 Dev(u8),
1830 Prod(Option<SocketAddr>),
1831 }
1832
1833 impl GatewayAddress {
1834 fn ip(&self) -> Option<SocketAddr> {
1835 if let GatewayAddress::Prod(ip) = self {
1836 return *ip;
1837 }
1838 None
1839 }
1840
1841 fn port(&self) -> Option<u16> {
1842 if let GatewayAddress::Dev(port) = self {
1843 return Some(*port as u16);
1844 }
1845 None
1846 }
1847 }
1848
1849 impl Arbitrary for Gateway<CurrentNetwork> {
1850 type Parameters = ();
1851 type Strategy = BoxedStrategy<Gateway<CurrentNetwork>>;
1852
1853 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1854 any_valid_dev_gateway()
1855 .prop_map(|(storage, _, private_key, address)| {
1856 Gateway::new(
1857 Account::try_from(private_key).unwrap(),
1858 storage.clone(),
1859 storage.ledger().clone(),
1860 address.ip(),
1861 &[],
1862 false,
1863 NodeDataDir::new_test(None),
1864 address.port(),
1865 )
1866 .unwrap()
1867 })
1868 .boxed()
1869 }
1870 }
1871
1872 type GatewayInput = (Storage<CurrentNetwork>, CommitteeContext, PrivateKey<CurrentNetwork>, GatewayAddress);
1873
1874 fn any_valid_dev_gateway() -> BoxedStrategy<GatewayInput> {
1875 (any::<CommitteeContext>(), any::<Selector>())
1876 .prop_flat_map(|(context, account_selector)| {
1877 let CommitteeContext(_, ValidatorSet(validators)) = context.clone();
1878 (
1879 any_with::<Storage<CurrentNetwork>>(context.clone()),
1880 Just(context),
1881 Just(account_selector.select(validators)),
1882 0u8..,
1883 )
1884 .prop_map(|(a, b, c, d)| (a, b, c.private_key, GatewayAddress::Dev(d)))
1885 })
1886 .boxed()
1887 }
1888
1889 fn any_valid_prod_gateway() -> BoxedStrategy<GatewayInput> {
1890 (any::<CommitteeContext>(), any::<Selector>())
1891 .prop_flat_map(|(context, account_selector)| {
1892 let CommitteeContext(_, ValidatorSet(validators)) = context.clone();
1893 (
1894 any_with::<Storage<CurrentNetwork>>(context.clone()),
1895 Just(context),
1896 Just(account_selector.select(validators)),
1897 any::<Option<SocketAddr>>(),
1898 )
1899 .prop_map(|(a, b, c, d)| (a, b, c.private_key, GatewayAddress::Prod(d)))
1900 })
1901 .boxed()
1902 }
1903
1904 #[proptest]
1905 fn gateway_dev_initialization(#[strategy(any_valid_dev_gateway())] input: GatewayInput) {
1906 let (storage, _, private_key, dev) = input;
1907 let account = Account::try_from(private_key).unwrap();
1908
1909 let gateway = Gateway::new(
1910 account.clone(),
1911 storage.clone(),
1912 storage.ledger().clone(),
1913 dev.ip(),
1914 &[],
1915 false,
1916 NodeDataDir::new_test(None),
1917 dev.port(),
1918 )
1919 .unwrap();
1920 let tcp_config = gateway.tcp().config();
1921 assert_eq!(tcp_config.listener_ip, Some(IpAddr::V4(Ipv4Addr::LOCALHOST)));
1922 assert_eq!(tcp_config.desired_listening_port, Some(MEMORY_POOL_PORT + dev.port().unwrap()));
1923
1924 let tcp_config = gateway.tcp().config();
1925 assert_eq!(tcp_config.max_connections, Committee::<CurrentNetwork>::max_committee_size() * 10);
1926 assert_eq!(gateway.account().address(), account.address());
1927 }
1928
1929 #[proptest]
1930 fn gateway_prod_initialization(#[strategy(any_valid_prod_gateway())] input: GatewayInput) {
1931 let (storage, _, private_key, dev) = input;
1932 let account = Account::try_from(private_key).unwrap();
1933
1934 let gateway = Gateway::new(
1935 account.clone(),
1936 storage.clone(),
1937 storage.ledger().clone(),
1938 dev.ip(),
1939 &[],
1940 false,
1941 NodeDataDir::new_test(None),
1942 dev.port(),
1943 )
1944 .unwrap();
1945 let tcp_config = gateway.tcp().config();
1946 if let Some(socket_addr) = dev.ip() {
1947 assert_eq!(tcp_config.listener_ip, Some(socket_addr.ip()));
1948 assert_eq!(tcp_config.desired_listening_port, Some(socket_addr.port()));
1949 } else {
1950 assert_eq!(tcp_config.listener_ip, Some(IpAddr::V4(Ipv4Addr::UNSPECIFIED)));
1951 assert_eq!(tcp_config.desired_listening_port, Some(MEMORY_POOL_PORT));
1952 }
1953
1954 let tcp_config = gateway.tcp().config();
1955 assert_eq!(tcp_config.max_connections, Committee::<CurrentNetwork>::max_committee_size() * 10);
1956 assert_eq!(gateway.account().address(), account.address());
1957 }
1958
1959 #[proptest(async = "tokio")]
1960 async fn gateway_start(
1961 #[strategy(any_valid_dev_gateway())] input: GatewayInput,
1962 #[strategy(0..MAX_WORKERS)] workers_count: u8,
1963 ) {
1964 let (storage, committee, private_key, dev) = input;
1965 let committee = committee.0;
1966 let worker_storage = storage.clone();
1967 let account = Account::try_from(private_key).unwrap();
1968
1969 let gateway = Gateway::new(
1970 account,
1971 storage.clone(),
1972 storage.ledger().clone(),
1973 dev.ip(),
1974 &[],
1975 false,
1976 NodeDataDir::new_test(None),
1977 dev.port(),
1978 )
1979 .unwrap();
1980
1981 let (primary_sender, _) = init_primary_channels();
1982
1983 let (workers, worker_senders) = {
1984 let mut tx_workers = IndexMap::new();
1986 let mut workers = IndexMap::new();
1987
1988 for id in 0..workers_count {
1990 let (tx_worker, rx_worker) = init_worker_channels();
1992 let ledger = Arc::new(MockLedgerService::new(committee.clone()));
1994 let worker =
1995 Worker::new(id, Arc::new(gateway.clone()), worker_storage.clone(), ledger, Default::default())
1996 .unwrap();
1997 worker.run(rx_worker);
1999
2000 workers.insert(id, worker);
2002 tx_workers.insert(id, tx_worker);
2003 }
2004 (workers, tx_workers)
2005 };
2006
2007 gateway.run(primary_sender, worker_senders, None).await;
2008 assert_eq!(
2009 gateway.local_ip(),
2010 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), MEMORY_POOL_PORT + dev.port().unwrap())
2011 );
2012 assert_eq!(gateway.num_workers(), workers.len() as u8);
2013 }
2014
2015 #[proptest]
2016 fn test_is_authorized_validator(#[strategy(any_valid_dev_gateway())] input: GatewayInput) {
2017 let rng = &mut TestRng::default();
2018
2019 let current_round = 2;
2021 let committee_size = 4;
2022 let max_gc_rounds = BatchHeader::<CurrentNetwork>::MAX_GC_ROUNDS as u64;
2023 let (_, _, private_key, dev) = input;
2024 let account = Account::try_from(private_key).unwrap();
2025
2026 let mut certificates = IndexSet::new();
2028 for _ in 0..committee_size {
2029 certificates.insert(sample_batch_certificate_for_round(current_round, rng));
2030 }
2031 let addresses: Vec<_> = certificates.iter().map(|certificate| certificate.author()).collect();
2032 let committee = sample_committee_for_round_and_members(current_round, addresses, rng);
2034 for _ in 0..committee_size {
2036 certificates.insert(sample_batch_certificate_for_round(current_round, rng));
2037 }
2038 let ledger = Arc::new(MockLedgerService::new(committee.clone()));
2040 let storage = Storage::new(ledger.clone(), Arc::new(BFTMemoryService::new()), max_gc_rounds).unwrap();
2042 let gateway = Gateway::new(
2044 account.clone(),
2045 storage.clone(),
2046 ledger.clone(),
2047 dev.ip(),
2048 &[],
2049 false,
2050 NodeDataDir::new_test(None),
2051 dev.port(),
2052 )
2053 .unwrap();
2054 for certificate in certificates.iter() {
2056 storage.testing_only_insert_certificate_testing_only(certificate.clone());
2057 }
2058 for i in 0..certificates.clone().len() {
2060 let is_authorized = gateway.is_authorized_validator_address(certificates[i].author());
2061 if i < committee_size {
2062 assert!(is_authorized);
2063 } else {
2064 assert!(!is_authorized);
2065 }
2066 }
2067 }
2068}