diff --git a/p2p/src/peer_manager/tests/addr_list_response_caching.rs b/p2p/src/peer_manager/tests/addr_list_response_caching.rs index a92eb6dd26..fd1d698854 100644 --- a/p2p/src/peer_manager/tests/addr_list_response_caching.rs +++ b/p2p/src/peer_manager/tests/addr_list_response_caching.rs @@ -247,6 +247,7 @@ fn make_p2p_config() -> P2pConfig { msg_max_locator_count: Default::default(), max_message_size: Default::default(), max_peer_tx_announcements: Default::default(), + ..Default::default() }, bind_addresses: Default::default(), diff --git a/p2p/src/protocol.rs b/p2p/src/protocol.rs index 34a167f055..d7d06e1d9d 100644 --- a/p2p/src/protocol.rs +++ b/p2p/src/protocol.rs @@ -88,6 +88,12 @@ make_config_setting!(MaxMessageSize, usize, 10 * 1024 * 1024); make_config_setting!(MaxPeerTxAnnouncements, usize, 5000); make_config_setting!(MaxUnconnectedHeaders, usize, 10); make_config_setting!(MaxAddrListResponseAddressCount, usize, 1000); +make_config_setting!(ForkDownloadLimit, usize, 2000); +make_config_setting!( + ForkDownloadRefillInterval, + std::time::Duration, + std::time::Duration::from_secs(600) +); /// Protocol configuration. These values are supposed to be modified in tests only. /// @@ -115,4 +121,10 @@ pub struct ProtocolConfig { pub max_message_size: MaxMessageSize, /// The maximum number of announcements (hashes) for which we haven't received transactions. pub max_peer_tx_announcements: MaxPeerTxAnnouncements, + /// The maximum number of blocks that a peer can make us download for the header lists + /// it announces, before further announced header lists are deferred until the budget + /// is refilled. + pub max_fork_downloads_per_peer: ForkDownloadLimit, + /// How often the per-peer fork download budget is refilled. + pub fork_download_refill_interval: ForkDownloadRefillInterval, } diff --git a/p2p/src/sync/peer/block_manager.rs b/p2p/src/sync/peer/block_manager.rs index e20d5107af..5031f22a64 100644 --- a/p2p/src/sync/peer/block_manager.rs +++ b/p2p/src/sync/peer/block_manager.rs @@ -16,6 +16,7 @@ use std::{ collections::{BTreeSet, VecDeque}, mem, + time::Duration, }; use itertools::Itertools; @@ -63,6 +64,8 @@ use crate::{ utils::oneshot_nofail, }; +use super::fork_download_budget::ForkDownloadBudget; + #[derive(Debug, Clone)] pub enum PeerBlockSyncManagerLocalEvent { /// Chainstate got new tip. @@ -94,8 +97,26 @@ pub struct PeerBlockSyncManager { /// of headers less than the maximum. This is the signal to the peer that we have no more /// headers, so it may not ask us for more of them in the future. have_sent_all_headers: bool, + /// A per-peer budget limiting the total amount of fork (non-tip) block downloads that + /// this peer can cause us to perform. See `ForkDownloadBudget` for the rationale. + fork_download_budget: ForkDownloadBudget, + /// The number of consecutive header lists that contained no new headers while claiming + /// (by their size) that the peer may have more of them. Used to stop serving peers that + /// keep sending already-known header lists, which would otherwise make us issue header + /// requests forever, at no cost to them. + consecutive_known_full_header_lists: u32, + /// If set, a header list has been deferred due to the fork download budget and we + /// should ask the peer for its headers again once the given time is reached. Without + /// this, a deferred list would never be fetched unless the peer re-announces, which + /// cannot be relied upon (e.g. a quiet upstream). + fork_budget_retry_at: Option