@@ -513,9 +513,9 @@ class PeerManagerImpl final : public PeerManager
513
513
514
514
/* * Implement NetEventsInterface */
515
515
void InitializeNode (CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
516
- void FinalizeNode (const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
516
+ void FinalizeNode (const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex );
517
517
bool ProcessMessages (CNode* pfrom, std::atomic<bool >& interrupt) override
518
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
518
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex );
519
519
bool SendMessages (CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing)
520
520
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
521
521
@@ -532,7 +532,7 @@ class PeerManagerImpl final : public PeerManager
532
532
void UnitTestMisbehaving (NodeId peer_id, int howmuch) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving (*Assert (GetPeerRef (peer_id)), howmuch, " " ); };
533
533
void ProcessMessage (CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
534
534
const std::chrono::microseconds time_received, const std::atomic<bool >& interruptMsgProc) override
535
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
535
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex );
536
536
void UpdateLastBlockAnnounceTime (NodeId node, int64_t time_in_seconds) override ;
537
537
538
538
private:
@@ -601,7 +601,7 @@ class PeerManagerImpl final : public PeerManager
601
601
void ProcessHeadersMessage (CNode& pfrom, Peer& peer,
602
602
std::vector<CBlockHeader>&& headers,
603
603
bool via_compact_block)
604
- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
604
+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex );
605
605
/* * Various helpers for headers processing, invoked by ProcessHeadersMessage() */
606
606
/* * Return true if headers are continuous and have valid proof-of-work (DoS points assigned on failure) */
607
607
bool CheckHeadersPoW (const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, Peer& peer);
@@ -633,7 +633,7 @@ class PeerManagerImpl final : public PeerManager
633
633
*/
634
634
bool IsContinuationOfLowWorkHeadersSync (Peer& peer, CNode& pfrom,
635
635
std::vector<CBlockHeader>& headers)
636
- EXCLUSIVE_LOCKS_REQUIRED(peer.m_headers_sync_mutex);
636
+ EXCLUSIVE_LOCKS_REQUIRED(peer.m_headers_sync_mutex, !m_headers_presync_mutex );
637
637
/* * Check work on a headers chain to be processed, and if insufficient,
638
638
* initiate our anti-DoS headers sync mechanism.
639
639
*
@@ -649,7 +649,7 @@ class PeerManagerImpl final : public PeerManager
649
649
bool TryLowWorkHeadersSync (Peer& peer, CNode& pfrom,
650
650
const CBlockIndex* chain_start_header,
651
651
std::vector<CBlockHeader>& headers)
652
- EXCLUSIVE_LOCKS_REQUIRED(!peer.m_headers_sync_mutex, !m_peer_mutex);
652
+ EXCLUSIVE_LOCKS_REQUIRED(!peer.m_headers_sync_mutex, !m_peer_mutex, !m_headers_presync_mutex );
653
653
654
654
/* * Return true if the given header is an ancestor of
655
655
* m_chainman.m_best_header or our current tip */
@@ -844,6 +844,24 @@ class PeerManagerImpl final : public PeerManager
844
844
std::shared_ptr<const CBlockHeaderAndShortTxIDs> m_most_recent_compact_block GUARDED_BY (m_most_recent_block_mutex);
845
845
uint256 m_most_recent_block_hash GUARDED_BY (m_most_recent_block_mutex);
846
846
847
+ // Data about the low-work headers synchronization, aggregated from all peers' HeadersSyncStates.
848
+ /* * Mutex guarding the other m_headers_presync_* variables. */
849
+ Mutex m_headers_presync_mutex;
850
+ /* * A type to represent statistics about a peer's low-work headers sync.
851
+ *
852
+ * - The first field is the total verified amount of work in that synchronization.
853
+ * - The second is:
854
+ * - nullopt: the sync is in REDOWNLOAD phase (phase 2).
855
+ * - {height, timestamp}: the sync has the specified tip height and block timestamp (phase 1).
856
+ */
857
+ using HeadersPresyncStats = std::pair<arith_uint256, std::optional<std::pair<int64_t , uint32_t >>>;
858
+ /* * Statistics for all peers in low-work headers sync. */
859
+ std::map<NodeId, HeadersPresyncStats> m_headers_presync_stats GUARDED_BY (m_headers_presync_mutex) {};
860
+ /* * The peer with the most-work entry in m_headers_presync_stats. */
861
+ NodeId m_headers_presync_bestpeer GUARDED_BY (m_headers_presync_mutex) {-1 };
862
+ /* * The m_headers_presync_stats improved, and needs signalling. */
863
+ std::atomic_bool m_headers_presync_should_signal{false };
864
+
847
865
/* * Height of the highest block announced using BIP 152 high-bandwidth mode. */
848
866
int m_highest_fast_announce{0 };
849
867
@@ -1502,6 +1520,10 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
1502
1520
// fSuccessfullyConnected set.
1503
1521
m_addrman.Connected (node.addr );
1504
1522
}
1523
+ {
1524
+ LOCK (m_headers_presync_mutex);
1525
+ m_headers_presync_stats.erase (nodeid);
1526
+ }
1505
1527
LogPrint (BCLog::NET, " Cleared nodestate for peer=%d\n " , nodeid);
1506
1528
}
1507
1529
@@ -2448,6 +2470,48 @@ bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfro
2448
2470
2449
2471
if (peer.m_headers_sync ->GetState () == HeadersSyncState::State::FINAL) {
2450
2472
peer.m_headers_sync .reset (nullptr );
2473
+
2474
+ // Delete this peer's entry in m_headers_presync_stats.
2475
+ // If this is m_headers_presync_bestpeer, it will be replaced later
2476
+ // by the next peer that triggers the else{} branch below.
2477
+ LOCK (m_headers_presync_mutex);
2478
+ m_headers_presync_stats.erase (pfrom.GetId ());
2479
+ } else {
2480
+ // Build statistics for this peer's sync.
2481
+ HeadersPresyncStats stats;
2482
+ stats.first = peer.m_headers_sync ->GetPresyncWork ();
2483
+ if (peer.m_headers_sync ->GetState () == HeadersSyncState::State::PRESYNC) {
2484
+ stats.second = {peer.m_headers_sync ->GetPresyncHeight (),
2485
+ peer.m_headers_sync ->GetPresyncTime ()};
2486
+ }
2487
+
2488
+ // Update statistics in stats.
2489
+ LOCK (m_headers_presync_mutex);
2490
+ m_headers_presync_stats[pfrom.GetId ()] = stats;
2491
+ auto best_it = m_headers_presync_stats.find (m_headers_presync_bestpeer);
2492
+ bool best_updated = false ;
2493
+ if (best_it == m_headers_presync_stats.end ()) {
2494
+ // If the cached best peer is outdated, iterate over all remaining ones (including
2495
+ // newly updated one) to find the best one.
2496
+ NodeId peer_best{-1 };
2497
+ const HeadersPresyncStats* stat_best{nullptr };
2498
+ for (const auto & [peer, stat] : m_headers_presync_stats) {
2499
+ if (!stat_best || stat > *stat_best) {
2500
+ peer_best = peer;
2501
+ stat_best = &stat;
2502
+ }
2503
+ }
2504
+ m_headers_presync_bestpeer = peer_best;
2505
+ best_updated = (peer_best == pfrom.GetId ());
2506
+ } else if (best_it->first == pfrom.GetId () || stats > best_it->second ) {
2507
+ // pfrom was and remains the best peer, or pfrom just became best.
2508
+ m_headers_presync_bestpeer = pfrom.GetId ();
2509
+ best_updated = true ;
2510
+ }
2511
+ if (best_updated && stats.second .has_value ()) {
2512
+ // If the best peer updated, and it is in its first phase, signal.
2513
+ m_headers_presync_should_signal = true ;
2514
+ }
2451
2515
}
2452
2516
2453
2517
if (result.success ) {
@@ -2676,6 +2740,8 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
2676
2740
LOCK (peer.m_headers_sync_mutex );
2677
2741
if (peer.m_headers_sync ) {
2678
2742
peer.m_headers_sync .reset (nullptr );
2743
+ LOCK (m_headers_presync_mutex);
2744
+ m_headers_presync_stats.erase (pfrom.GetId ());
2679
2745
}
2680
2746
return ;
2681
2747
}
@@ -4318,7 +4384,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4318
4384
ReadCompactSize (vRecv); // ignore tx count; assume it is 0.
4319
4385
}
4320
4386
4321
- return ProcessHeadersMessage (pfrom, *peer, std::move (headers), /* via_compact_block=*/ false );
4387
+ ProcessHeadersMessage (pfrom, *peer, std::move (headers), /* via_compact_block=*/ false );
4388
+
4389
+ // Check if the headers presync progress needs to be reported to validation.
4390
+ // This needs to be done without holding the m_headers_presync_mutex lock.
4391
+ if (m_headers_presync_should_signal.exchange (false )) {
4392
+ HeadersPresyncStats stats;
4393
+ {
4394
+ LOCK (m_headers_presync_mutex);
4395
+ auto it = m_headers_presync_stats.find (m_headers_presync_bestpeer);
4396
+ if (it != m_headers_presync_stats.end ()) stats = it->second ;
4397
+ }
4398
+ if (stats.second ) {
4399
+ m_chainman.ReportHeadersPresync (stats.first , stats.second ->first , stats.second ->second );
4400
+ }
4401
+ }
4402
+
4403
+ return ;
4322
4404
}
4323
4405
4324
4406
if (msg_type == NetMsgType::BLOCK)
0 commit comments