@@ -513,9 +513,9 @@ class PeerManagerImpl final : public PeerManager
513513
514514 /* * Implement NetEventsInterface */
515515 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 );
517517 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 );
519519 bool SendMessages (CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing)
520520 EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex);
521521
@@ -532,7 +532,7 @@ class PeerManagerImpl final : public PeerManager
532532 void UnitTestMisbehaving (NodeId peer_id, int howmuch) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving (*Assert (GetPeerRef (peer_id)), howmuch, " " ); };
533533 void ProcessMessage (CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
534534 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 );
536536 void UpdateLastBlockAnnounceTime (NodeId node, int64_t time_in_seconds) override ;
537537
538538private:
@@ -601,7 +601,7 @@ class PeerManagerImpl final : public PeerManager
601601 void ProcessHeadersMessage (CNode& pfrom, Peer& peer,
602602 std::vector<CBlockHeader>&& headers,
603603 bool via_compact_block)
604- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
604+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex );
605605 /* * Various helpers for headers processing, invoked by ProcessHeadersMessage() */
606606 /* * Return true if headers are continuous and have valid proof-of-work (DoS points assigned on failure) */
607607 bool CheckHeadersPoW (const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, Peer& peer);
@@ -633,7 +633,7 @@ class PeerManagerImpl final : public PeerManager
633633 */
634634 bool IsContinuationOfLowWorkHeadersSync (Peer& peer, CNode& pfrom,
635635 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 );
637637 /* * Check work on a headers chain to be processed, and if insufficient,
638638 * initiate our anti-DoS headers sync mechanism.
639639 *
@@ -649,7 +649,7 @@ class PeerManagerImpl final : public PeerManager
649649 bool TryLowWorkHeadersSync (Peer& peer, CNode& pfrom,
650650 const CBlockIndex* chain_start_header,
651651 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 );
653653
654654 /* * Return true if the given header is an ancestor of
655655 * m_chainman.m_best_header or our current tip */
@@ -844,6 +844,24 @@ class PeerManagerImpl final : public PeerManager
844844 std::shared_ptr<const CBlockHeaderAndShortTxIDs> m_most_recent_compact_block GUARDED_BY (m_most_recent_block_mutex);
845845 uint256 m_most_recent_block_hash GUARDED_BY (m_most_recent_block_mutex);
846846
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+
847865 /* * Height of the highest block announced using BIP 152 high-bandwidth mode. */
848866 int m_highest_fast_announce{0 };
849867
@@ -1502,6 +1520,10 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
15021520 // fSuccessfullyConnected set.
15031521 m_addrman.Connected (node.addr );
15041522 }
1523+ {
1524+ LOCK (m_headers_presync_mutex);
1525+ m_headers_presync_stats.erase (nodeid);
1526+ }
15051527 LogPrint (BCLog::NET, " Cleared nodestate for peer=%d\n " , nodeid);
15061528}
15071529
@@ -2448,6 +2470,48 @@ bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfro
24482470
24492471 if (peer.m_headers_sync ->GetState () == HeadersSyncState::State::FINAL) {
24502472 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+ }
24512515 }
24522516
24532517 if (result.success ) {
@@ -2676,6 +2740,8 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
26762740 LOCK (peer.m_headers_sync_mutex );
26772741 if (peer.m_headers_sync ) {
26782742 peer.m_headers_sync .reset (nullptr );
2743+ LOCK (m_headers_presync_mutex);
2744+ m_headers_presync_stats.erase (pfrom.GetId ());
26792745 }
26802746 return ;
26812747 }
@@ -4318,7 +4384,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43184384 ReadCompactSize (vRecv); // ignore tx count; assume it is 0.
43194385 }
43204386
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 ;
43224404 }
43234405
43244406 if (msg_type == NetMsgType::BLOCK)
0 commit comments