@@ -10,12 +10,6 @@ use tokio::sync::{mpsc, Mutex};
1010use tokio:: task:: JoinSet ;
1111use tokio:: time;
1212
13- use async_trait:: async_trait;
14- use dashcore:: network:: constants:: ServiceFlags ;
15- use dashcore:: network:: message:: NetworkMessage ;
16- use dashcore:: Network ;
17- use tokio_util:: sync:: CancellationToken ;
18-
1913use crate :: client:: config:: MempoolStrategy ;
2014use crate :: client:: ClientConfig ;
2115use crate :: error:: { NetworkError , NetworkResult , SpvError as Error } ;
@@ -29,6 +23,12 @@ use crate::network::reputation::{
2923} ;
3024use crate :: network:: { HandshakeManager , NetworkManager , Peer } ;
3125use crate :: types:: PeerInfo ;
26+ use async_trait:: async_trait;
27+ use dashcore:: network:: constants:: ServiceFlags ;
28+ use dashcore:: network:: message:: NetworkMessage ;
29+ use dashcore:: network:: message_headers2:: CompressionState ;
30+ use dashcore:: Network ;
31+ use tokio_util:: sync:: CancellationToken ;
3232
3333/// Peer network manager
3434pub struct PeerNetworkManager {
@@ -71,6 +71,8 @@ pub struct PeerNetworkManager {
7171 exclusive_mode : bool ,
7272 /// Cached count of currently connected peers for fast, non-blocking queries
7373 connected_peer_count : Arc < AtomicUsize > ,
74+ /// Disable headers2 after decompression failure
75+ headers2_disabled : Arc < Mutex < HashSet < SocketAddr > > > ,
7476}
7577
7678impl PeerNetworkManager {
@@ -124,6 +126,7 @@ impl PeerNetworkManager {
124126 user_agent : config. user_agent . clone ( ) ,
125127 exclusive_mode,
126128 connected_peer_count : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
129+ headers2_disabled : Arc :: new ( Mutex :: new ( HashSet :: new ( ) ) ) ,
127130 } )
128131 }
129132
@@ -210,6 +213,7 @@ impl PeerNetworkManager {
210213 let mempool_strategy = self . mempool_strategy ;
211214 let user_agent = self . user_agent . clone ( ) ;
212215 let connected_peer_count = self . connected_peer_count . clone ( ) ;
216+ let headers2_disabled = self . headers2_disabled . clone ( ) ;
213217
214218 // Spawn connection task
215219 let mut tasks = self . tasks . lock ( ) . await ;
@@ -249,6 +253,7 @@ impl PeerNetworkManager {
249253 shutdown_token,
250254 reputation_manager. clone ( ) ,
251255 connected_peer_count. clone ( ) ,
256+ headers2_disabled. clone ( ) ,
252257 )
253258 . await ;
254259 }
@@ -283,6 +288,7 @@ impl PeerNetworkManager {
283288 }
284289
285290 /// Start reading messages from a peer
291+ #[ allow( clippy:: too_many_arguments) ] // TODO: refactor to reduce arguments
286292 async fn start_peer_reader (
287293 addr : SocketAddr ,
288294 pool : Arc < PeerPool > ,
@@ -291,10 +297,12 @@ impl PeerNetworkManager {
291297 shutdown_token : CancellationToken ,
292298 reputation_manager : Arc < PeerReputationManager > ,
293299 connected_peer_count : Arc < AtomicUsize > ,
300+ headers2_disabled : Arc < Mutex < HashSet < SocketAddr > > > ,
294301 ) {
295302 tokio:: spawn ( async move {
296303 log:: debug!( "Starting peer reader loop for {}" , addr) ;
297304 let mut loop_iteration = 0 ;
305+ let mut headers2_state = CompressionState :: default ( ) ;
298306
299307 loop {
300308 loop_iteration += 1 ;
@@ -440,9 +448,49 @@ impl PeerNetworkManager {
440448 // Forward to client
441449 }
442450 NetworkMessage :: Headers2 ( headers2) => {
443- // Log compressed headers messages specifically
444- log:: info!( "📨 Received Headers2 message from {} with {} compressed headers!" , addr, headers2. headers. len( ) ) ;
445- // Forward to client (decompression handled by sync manager)
451+ // Decompress headers in network layer and forward as regular Headers
452+ log:: info!(
453+ "Received Headers2 from {} with {} compressed headers - decompressing" ,
454+ addr,
455+ headers2. headers. len( )
456+ ) ;
457+
458+ match headers2_state. process_headers ( & headers2. headers ) {
459+ Ok ( headers) => {
460+ log:: info!(
461+ "Decompressed {} headers from {} - forwarding as regular Headers" ,
462+ headers. len( ) ,
463+ addr
464+ ) ;
465+ // Forward as regular Headers message
466+ let headers_msg = NetworkMessage :: Headers ( headers) ;
467+ if message_tx. send ( ( addr, headers_msg) ) . await . is_err ( ) {
468+ log:: warn!(
469+ "Breaking peer reader loop for {} - failed to send decompressed headers" ,
470+ addr
471+ ) ;
472+ break ;
473+ }
474+ continue ; // Already sent, don't forward the original Headers2
475+ }
476+ Err ( e) => {
477+ log:: error!(
478+ "Headers2 decompression failed from {}: {} - disabling headers2" ,
479+ addr,
480+ e
481+ ) ;
482+ headers2_disabled. lock ( ) . await . insert ( addr) ;
483+ // Apply reputation penalty
484+ reputation_manager
485+ . update_reputation (
486+ addr,
487+ misbehavior_scores:: INVALID_MESSAGE ,
488+ "Headers2 decompression failed" ,
489+ )
490+ . await ;
491+ continue ; // Don't forward corrupted message
492+ }
493+ }
446494 }
447495 NetworkMessage :: GetHeaders ( _) => {
448496 // SPV clients don't serve headers to peers
@@ -573,6 +621,8 @@ impl PeerNetworkManager {
573621 connected_peer_count. fetch_sub ( 1 , Ordering :: Relaxed ) ;
574622 }
575623
624+ headers2_disabled. lock ( ) . await . remove ( & addr) ;
625+
576626 // Give small positive reputation if peer maintained long connection
577627 let conn_duration = Duration :: from_secs ( 60 * loop_iteration) ; // Rough estimate
578628 if conn_duration > Duration :: from_secs ( 3600 ) {
@@ -780,7 +830,8 @@ impl PeerNetworkManager {
780830 // For filter-related messages, we need a peer that supports compact filters
781831 let requires_compact_filters =
782832 matches ! ( & message, NetworkMessage :: GetCFHeaders ( _) | NetworkMessage :: GetCFilters ( _) ) ;
783- let requires_headers2 = matches ! ( & message, NetworkMessage :: GetHeaders2 ( _) ) ;
833+ let check_headers2 =
834+ matches ! ( & message, NetworkMessage :: GetHeaders ( _) | NetworkMessage :: GetHeaders2 ( _) ) ;
784835
785836 let selected_peer = if requires_compact_filters {
786837 // Find a peer that supports compact filters
@@ -808,7 +859,7 @@ impl PeerNetworkManager {
808859 ) ) ;
809860 }
810861 }
811- } else if requires_headers2 {
862+ } else if check_headers2 {
812863 // Prefer a peer that advertises headers2 support
813864 let mut current_sync_peer = self . current_sync_peer . lock ( ) . await ;
814865 let mut selected: Option < SocketAddr > = None ;
@@ -875,6 +926,25 @@ impl PeerNetworkManager {
875926 . find ( |( a, _) | * a == selected_peer)
876927 . ok_or_else ( || NetworkError :: ConnectionFailed ( "Selected peer not found" . to_string ( ) ) ) ?;
877928
929+ // Upgrade GetHeaders to GetHeaders2 if this specific peer supports it and not disabled
930+ let peer_supports_headers2 = {
931+ let peer_guard = peer. read ( ) . await ;
932+ peer_guard. can_request_headers2 ( )
933+ } ;
934+ let message = match message {
935+ NetworkMessage :: GetHeaders ( get_headers)
936+ if !self . headers2_disabled . lock ( ) . await . contains ( addr)
937+ && peer_supports_headers2 =>
938+ {
939+ log:: debug!(
940+ "Upgrading GetHeaders to GetHeaders2 for peer {}: {:?}" ,
941+ addr,
942+ get_headers
943+ ) ;
944+ NetworkMessage :: GetHeaders2 ( get_headers)
945+ }
946+ other => other,
947+ } ;
878948 // Reduce verbosity for common sync messages
879949 match & message {
880950 NetworkMessage :: GetHeaders ( _)
@@ -1069,6 +1139,7 @@ impl Clone for PeerNetworkManager {
10691139 user_agent : self . user_agent . clone ( ) ,
10701140 exclusive_mode : self . exclusive_mode ,
10711141 connected_peer_count : self . connected_peer_count . clone ( ) ,
1142+ headers2_disabled : self . headers2_disabled . clone ( ) ,
10721143 }
10731144 }
10741145}
0 commit comments