smoldot_light/
network_service.rs

1// Smoldot
2// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18//! Background network service.
19//!
20//! The [`NetworkService`] manages background tasks dedicated to connecting to other nodes.
21//! Importantly, its design is oriented towards the particular use case of the light client.
22//!
23//! The [`NetworkService`] spawns one background task (using [`PlatformRef::spawn_task`]) for
24//! each active connection.
25//!
26//! The objective of the [`NetworkService`] in general is to try stay connected as much as
27//! possible to the nodes of the peer-to-peer network of the chain, and maintain open substreams
28//! with them in order to send out requests (e.g. block requests) and notifications (e.g. block
29//! announces).
30//!
31//! Connectivity to the network is performed in the background as an implementation detail of
32//! the service. The public API only allows emitting requests and notifications towards the
33//! already-connected nodes.
34//!
35//! After a [`NetworkService`] is created, one can add chains using [`NetworkService::add_chain`].
36//! If all references to a [`NetworkServiceChain`] are destroyed, the chain is automatically
37//! purged.
38//!
39//! An important part of the API is the list of channel receivers of [`Event`] returned by
40//! [`NetworkServiceChain::subscribe`]. These channels inform the foreground about updates to the
41//! network connectivity.
42
43use crate::{
44    log, metrics,
45    platform::{self, PlatformRef, address_parse},
46};
47
48use alloc::{
49    borrow::ToOwned as _,
50    boxed::Box,
51    collections::{BTreeMap, VecDeque},
52    format,
53    string::{String, ToString as _},
54    sync::Arc,
55    vec::{self, Vec},
56};
57use core::{cmp, mem, num::NonZero, pin::Pin, time::Duration};
58use futures_channel::oneshot;
59use futures_lite::FutureExt as _;
60use futures_util::{StreamExt as _, future, stream};
61use hashbrown::{HashMap, HashSet};
62use rand::seq::IteratorRandom as _;
63use rand_chacha::rand_core::SeedableRng as _;
64use smoldot::{
65    header,
66    informant::{BytesDisplay, HashDisplay},
67    libp2p::{
68        connection,
69        multiaddr::{self, Multiaddr},
70        peer_id,
71    },
72    network::{basic_peering_strategy, bitswap_peering_strategy, codec, service},
73};
74
75pub use codec::{AffinityFilter, CallProofRequestConfig, Role};
76use service::SendTopicAffinityError;
77pub use service::{
78    ChainId, EncodedMerkleProof, PeerId, QueueNotificationError, SendBitswapMessageError,
79};
80
81mod tasks;
82
83/// Configuration for a [`NetworkService`].
84pub struct Config<TPlat> {
85    /// Access to the platform's capabilities.
86    pub platform: TPlat,
87
88    /// Value sent back for the agent version when receiving an identification request.
89    pub identify_agent_version: String,
90
91    /// Capacity to allocate for the list of chains.
92    pub chains_capacity: usize,
93
94    /// Maximum number of connections that the service can open simultaneously. After this value
95    /// has been reached, a new connection can be opened after each
96    /// [`Config::connections_open_pool_restore_delay`].
97    pub connections_open_pool_size: u32,
98
99    /// Delay after which the service can open a new connection.
100    /// The delay is cumulative. If no connection has been opened for example for twice this
101    /// duration, then two connections can be opened at the same time, up to a maximum of
102    /// [`Config::connections_open_pool_size`].
103    pub connections_open_pool_restore_delay: Duration,
104}
105
106/// See [`NetworkService::add_chain`].
107///
108/// Note that this configuration is intentionally missing a field containing the bootstrap
109/// nodes of the chain. Bootstrap nodes are supposed to be added afterwards by calling
110/// [`NetworkServiceChain::discover`].
111pub struct ConfigChain {
112    /// Name of the chain, for logging purposes.
113    pub log_name: String,
114
115    /// Number of "out slots" of this chain. We establish simultaneously gossip substreams up to
116    /// this number of peers.
117    pub num_out_slots: usize,
118
119    /// Hash of the genesis block of the chain. Sent to other nodes in order to determine whether
120    /// the chains match.
121    ///
122    /// > **Note**: Be aware that this *must* be the *genesis* block, not any block known to be
123    /// >           in the chain.
124    pub genesis_block_hash: [u8; 32],
125
126    /// Number and hash of the current best block. Can later be updated with
127    /// [`NetworkServiceChain::set_local_best_block`].
128    pub best_block: (u64, [u8; 32]),
129
130    /// Optional identifier to insert into the networking protocol names. Used to differentiate
131    /// between chains with the same genesis hash.
132    pub fork_id: Option<String>,
133
134    /// Number of bytes of the block number in the networking protocol.
135    pub block_number_bytes: usize,
136
137    /// Must be `Some` if and only if the chain uses the GrandPa networking protocol. Contains the
138    /// number of the finalized block at the time of the initialization.
139    pub grandpa_protocol_finalized_block_height: Option<u64>,
140
141    /// If `true`, enables the statement store protocol.
142    pub enable_statement_protocol: bool,
143
144    /// Metrics of the chain.
145    pub metrics: Arc<metrics::ChainMetrics>,
146}
147
148pub struct NetworkService<TPlat: PlatformRef> {
149    /// Channel connected to the background service.
150    messages_tx: async_channel::Sender<ToBackground<TPlat>>,
151
152    /// See [`Config::platform`].
153    platform: TPlat,
154
155    /// Process-wide network metrics, shared with the background service.
156    metrics: Arc<metrics::NetworkMetrics>,
157}
158
159impl<TPlat: PlatformRef> NetworkService<TPlat> {
160    /// Initializes the network service with the given configuration.
161    pub fn new(config: Config<TPlat>) -> Arc<Self> {
162        let (main_messages_tx, main_messages_rx) = async_channel::bounded(4);
163
164        let metrics = Arc::new(metrics::NetworkMetrics::default());
165
166        let network = service::ChainNetwork::new(service::Config {
167            chains_capacity: config.chains_capacity,
168            connections_capacity: 32,
169            // Shortened from 8s: parallel dials hold slots until this fires.
170            handshake_timeout: Duration::from_secs(4),
171            randomness_seed: {
172                let mut seed = [0; 32];
173                config.platform.fill_random_bytes(&mut seed);
174                seed
175            },
176        });
177
178        // Spawn main task that processes the network service.
179        let (tasks_messages_tx, tasks_messages_rx) = async_channel::bounded(32);
180        let task = Box::pin(background_task(BackgroundTask {
181            randomness: rand_chacha::ChaCha20Rng::from_seed({
182                let mut seed = [0; 32];
183                config.platform.fill_random_bytes(&mut seed);
184                seed
185            }),
186            identify_agent_version: config.identify_agent_version,
187            tasks_messages_tx,
188            tasks_messages_rx: Box::pin(tasks_messages_rx),
189            peering_strategy: basic_peering_strategy::BasicPeeringStrategy::new(
190                basic_peering_strategy::Config {
191                    randomness_seed: {
192                        let mut seed = [0; 32];
193                        config.platform.fill_random_bytes(&mut seed);
194                        seed
195                    },
196                    peers_capacity: 50, // TODO: ?
197                    chains_capacity: config.chains_capacity,
198                },
199            ),
200            bitswap_peering_strategy: bitswap_peering_strategy::BitswapPeeringStrategy::new(
201                bitswap_peering_strategy::Config {
202                    randomness_seed: {
203                        let mut seed = [0; 32];
204                        config.platform.fill_random_bytes(&mut seed);
205                        seed
206                    },
207                    peers_capacity: 50, // TODO: hardcoded to the same value as `peering_strategy`.
208                },
209            ),
210            network,
211            connections_open_pool_size: config.connections_open_pool_size,
212            connections_open_pool_restore_delay: config.connections_open_pool_restore_delay,
213            num_recent_connection_opening: 0,
214            next_recent_connection_restore: None,
215            platform: config.platform.clone(),
216            metrics: metrics.clone(),
217            open_gossip_links: BTreeMap::new(),
218            chains_ever_gossip_connected: HashSet::with_capacity_and_hasher(4, Default::default()),
219            v2_statement_peers: HashMap::with_capacity_and_hasher(4, Default::default()),
220            current_affinity_filter: HashMap::with_capacity_and_hasher(4, Default::default()),
221            events_pending_send: VecDeque::with_capacity(4),
222            event_senders: either::Left(Vec::new()),
223            pending_new_subscriptions: Vec::new(),
224            bitswap_event_pending_send: None,
225            bitswap_connected_peers: 0,
226            bitswap_event_senders: either::Left(Vec::new()),
227            pending_new_bitswap_subscriptions: Vec::new(),
228            statement_event_pending_send: None,
229            statement_event_senders: either::Left(Vec::new()),
230            pending_new_statement_subscriptions: Vec::new(),
231            important_nodes: HashMap::with_capacity_and_hasher(16, Default::default()),
232            main_messages_rx: Box::pin(main_messages_rx),
233            messages_rx: stream::SelectAll::new(),
234            blocks_requests: HashMap::with_capacity_and_hasher(8, Default::default()),
235            grandpa_warp_sync_requests: HashMap::with_capacity_and_hasher(8, Default::default()),
236            storage_proof_requests: HashMap::with_capacity_and_hasher(8, Default::default()),
237            call_proof_requests: HashMap::with_capacity_and_hasher(8, Default::default()),
238            child_storage_proof_requests: HashMap::with_capacity_and_hasher(8, Default::default()),
239            chains_by_next_discovery: BTreeMap::new(),
240        }));
241
242        config.platform.spawn_task("network-service".into(), {
243            let platform = config.platform.clone();
244            async move {
245                task.await;
246                log!(&platform, Debug, "network", "shutdown");
247            }
248        });
249
250        Arc::new(NetworkService {
251            messages_tx: main_messages_tx,
252            platform: config.platform,
253            metrics,
254        })
255    }
256
257    /// Returns the process-wide network metrics.
258    pub fn metrics(&self) -> Arc<metrics::NetworkMetrics> {
259        self.metrics.clone()
260    }
261
262    /// Adds a chain to the list of chains that the network service connects to.
263    ///
264    /// Returns an object representing the chain and that allows interacting with it. If all
265    /// references to [`NetworkServiceChain`] are destroyed, the network service automatically
266    /// purges that chain.
267    pub fn add_chain(&self, config: ConfigChain) -> Arc<NetworkServiceChain<TPlat>> {
268        let (messages_tx, messages_rx) = async_channel::bounded(32);
269        let chain_metrics = config.metrics.clone();
270
271        // TODO: this code is hacky because we don't want to make `add_chain` async at the moment, because it's not convenient for lib.rs
272        self.platform.spawn_task("add-chain-message-send".into(), {
273            let config = service::ChainConfig {
274                grandpa_protocol_config: config.grandpa_protocol_finalized_block_height.map(
275                    |commit_finalized_height| service::GrandpaState {
276                        commit_finalized_height,
277                        round_number: 1,
278                        set_id: 0,
279                    },
280                ),
281                enable_statement_protocol: config.enable_statement_protocol,
282                fork_id: config.fork_id.clone(),
283                block_number_bytes: config.block_number_bytes,
284                best_hash: config.best_block.1,
285                best_number: config.best_block.0,
286                genesis_hash: config.genesis_block_hash,
287                role: Role::Light,
288                allow_inbound_block_requests: false,
289                user_data: Chain {
290                    log_name: config.log_name,
291                    block_number_bytes: config.block_number_bytes,
292                    num_out_slots: config.num_out_slots,
293                    num_references: NonZero::<usize>::new(1).unwrap(),
294                    next_discovery_period: Duration::from_secs(2),
295                    next_discovery_when: self.platform.now(),
296                    metrics: config.metrics,
297                },
298            };
299
300            let messages_tx = self.messages_tx.clone();
301            async move {
302                let _ = messages_tx
303                    .send(ToBackground::AddChain {
304                        messages_rx,
305                        config,
306                    })
307                    .await;
308            }
309        });
310
311        Arc::new(NetworkServiceChain {
312            _keep_alive_messages_tx: self.messages_tx.clone(),
313            messages_tx,
314            metrics: chain_metrics,
315            platform: self.platform.clone(),
316        })
317    }
318}
319
320pub struct NetworkServiceChain<TPlat: PlatformRef> {
321    /// Copy of [`NetworkService::messages_tx`]. Used in order to maintain the network service
322    /// background task alive.
323    _keep_alive_messages_tx: async_channel::Sender<ToBackground<TPlat>>,
324
325    /// Channel to send messages to the background task.
326    messages_tx: async_channel::Sender<ToBackgroundChain>,
327
328    /// Metrics of the chain.
329    metrics: Arc<metrics::ChainMetrics>,
330
331    /// See [`Config::platform`].
332    platform: TPlat,
333}
334
335/// Severity of a ban. See [`NetworkServiceChain::ban_and_disconnect`].
336#[derive(Debug, Copy, Clone, PartialEq, Eq)]
337pub enum BanSeverity {
338    Low,
339    High,
340}
341
342/// Reason for banning a peer. Printed in the logs and used as the `reason` label
343/// of the `networkPeerBansTotal` metric.
344// Strum derives are explained on [`crate::metrics::MetricLabel`]; `Display`
345// additionally prints the same string in log messages.
346#[derive(
347    Debug, Copy, Clone, PartialEq, Eq, strum::Display, strum::EnumIter, strum::IntoStaticStr,
348)]
349#[strum(serialize_all = "kebab-case")]
350pub enum BanReason {
351    BadBlockAnnounce,
352    BadChildTrieRoot,
353    BadGrandpaCommit,
354    BadJustification,
355    BadMerkleProof,
356    BadWarpSyncFragment,
357    InvalidCallProof,
358    BlocksRequestFailed,
359    CallProofRequestFailed,
360    ChildStorageRequestFailed,
361    StorageRequestFailed,
362    WarpSyncRequestFailed,
363}
364
365impl<TPlat: PlatformRef> NetworkServiceChain<TPlat> {
366    /// Subscribes to the networking events that happen on the given chain.
367    ///
368    /// Calling this function returns a `Receiver` that receives events about the chain.
369    /// The new channel will immediately receive events about all the existing connections, so
370    /// that it is able to maintain a coherent view of the network.
371    ///
372    /// Note that this function is `async`, but it should return very quickly.
373    ///
374    /// The `Receiver` **must** be polled continuously. When the channel is full, the networking
375    /// connections will be back-pressured until the channel isn't full anymore.
376    ///
377    /// The `Receiver` never yields `None` unless the [`NetworkService`] crashes or is destroyed.
378    /// If `None` is yielded and the [`NetworkService`] is still alive, you should call
379    /// [`NetworkServiceChain::subscribe`] again to obtain a new `Receiver`.
380    ///
381    // TODO: consider not killing the background until the channel is destroyed, as that would be a more sensical behaviour
382    pub async fn subscribe(&self) -> async_channel::Receiver<Event> {
383        let (tx, rx) = async_channel::bounded(128);
384
385        self.messages_tx
386            .send(ToBackgroundChain::Subscribe { sender: tx })
387            .await
388            .unwrap();
389
390        rx
391    }
392
393    /// Subscribes to the Bitswap events that happen on the network. Bitswap events subscription is
394    /// separate from other network service events, because Bitswap events are big and are not
395    /// needed by the most of subscribers.
396    ///
397    /// Note that this function is `async`, but it should return very quickly.
398    ///
399    /// The `Receiver` **must** be polled continuously. When the channel is full, the networking
400    /// connections will be back-pressured until the channel isn't full anymore.
401    ///
402    /// The `Receiver` never yields `None` unless the [`NetworkService`] crashes or is destroyed.
403    /// If `None` is yielded and the [`NetworkService`] is still alive, you should call
404    /// [`NetworkServiceChain::subscribe_bitswap`] again to obtain a new `Receiver`.
405    ///
406    // TODO: the last section of the doc seem to contradict itself.
407    pub async fn subscribe_bitswap(&self) -> async_channel::Receiver<BitswapEvent> {
408        let (tx, rx) = async_channel::bounded(128);
409
410        self.messages_tx
411            .send(ToBackgroundChain::SubscribeBitswap { sender: tx })
412            .await
413            .unwrap();
414
415        rx
416    }
417
418    /// Subscribes to the statement notifications that happen on the network.
419    ///
420    /// Note that this function is `async`, but it should return very quickly.
421    ///
422    /// The `Receiver` **must** be polled continuously. When the channel is full, the networking
423    /// connections will be back-pressured until the channel isn't full anymore.
424    ///
425    /// The `Receiver` never yields `None` unless the [`NetworkService`] crashes or is destroyed.
426    /// If `None` is yielded and the [`NetworkService`] is still alive, you should call
427    /// [`NetworkServiceChain::subscribe_statements`] again to obtain a new `Receiver`.
428    pub async fn subscribe_statements(&self) -> async_channel::Receiver<StatementEvent> {
429        let (tx, rx) = async_channel::bounded(128);
430
431        self.messages_tx
432            .send(ToBackgroundChain::SubscribeStatements { sender: tx })
433            .await
434            .unwrap();
435
436        rx
437    }
438
439    /// Starts asynchronously disconnecting the given peer. A [`Event::Disconnected`] will later be
440    /// generated. Prevents a new gossip link with the same peer from being reopened for a
441    /// little while.
442    ///
443    /// `reason` is printed in the logs and counted in the metrics.
444    ///
445    /// Due to race conditions, it is possible to reconnect to the peer soon after, in case the
446    /// reconnection was already happening as the call to this function is still being processed.
447    /// If that happens another [`Event::Disconnected`] will be delivered afterwards. In other
448    /// words, this function guarantees that we will be disconnected in the future rather than
449    /// guarantees that we will disconnect.
450    pub async fn ban_and_disconnect(
451        &self,
452        peer_id: PeerId,
453        severity: BanSeverity,
454        reason: BanReason,
455    ) {
456        self.metrics.peer_bans.inc(reason);
457
458        let _ = self
459            .messages_tx
460            .send(ToBackgroundChain::DisconnectAndBan {
461                peer_id,
462                severity,
463                reason,
464            })
465            .await;
466    }
467
468    /// Sends a blocks request to the given peer.
469    // TODO: more docs
470    pub async fn blocks_request(
471        self: Arc<Self>,
472        target: PeerId,
473        config: codec::BlocksRequestConfig,
474        timeout: Duration,
475    ) -> Result<Vec<codec::BlockData>, BlocksRequestError> {
476        let when_started = self.platform.now();
477        let (tx, rx) = oneshot::channel();
478
479        self.messages_tx
480            .send(ToBackgroundChain::StartBlocksRequest {
481                target: target.clone(),
482                config,
483                timeout,
484                result: tx,
485            })
486            .await
487            .unwrap();
488
489        let result = rx.await.unwrap();
490        self.metrics
491            .blocks_requests
492            .observe(result.is_ok(), self.platform.now() - when_started);
493        result
494    }
495
496    /// Sends a grandpa warp sync request to the given peer.
497    // TODO: more docs
498    pub async fn grandpa_warp_sync_request(
499        self: Arc<Self>,
500        target: PeerId,
501        begin_hash: [u8; 32],
502        timeout: Duration,
503    ) -> Result<service::EncodedGrandpaWarpSyncResponse, WarpSyncRequestError> {
504        let when_started = self.platform.now();
505        let (tx, rx) = oneshot::channel();
506
507        self.messages_tx
508            .send(ToBackgroundChain::StartWarpSyncRequest {
509                target: target.clone(),
510                begin_hash,
511                timeout,
512                result: tx,
513            })
514            .await
515            .unwrap();
516
517        let result = rx.await.unwrap();
518        self.metrics
519            .warp_sync_requests
520            .observe(result.is_ok(), self.platform.now() - when_started);
521        result
522    }
523
524    pub async fn set_local_best_block(&self, best_hash: [u8; 32], best_number: u64) {
525        self.messages_tx
526            .send(ToBackgroundChain::SetLocalBestBlock {
527                best_hash,
528                best_number,
529            })
530            .await
531            .unwrap();
532    }
533
534    pub async fn set_local_grandpa_state(&self, grandpa_state: service::GrandpaState) {
535        self.messages_tx
536            .send(ToBackgroundChain::SetLocalGrandpaState { grandpa_state })
537            .await
538            .unwrap();
539    }
540
541    /// Sends a storage proof request to the given peer.
542    // TODO: more docs
543    pub async fn storage_proof_request(
544        self: Arc<Self>,
545        target: PeerId, // TODO: takes by value because of futures longevity issue
546        config: codec::StorageProofRequestConfig<impl Iterator<Item = impl AsRef<[u8]> + Clone>>,
547        timeout: Duration,
548    ) -> Result<service::EncodedMerkleProof, StorageProofRequestError> {
549        let when_started = self.platform.now();
550        let (tx, rx) = oneshot::channel();
551
552        self.messages_tx
553            .send(ToBackgroundChain::StartStorageProofRequest {
554                target: target.clone(),
555                config: codec::StorageProofRequestConfig {
556                    block_hash: config.block_hash,
557                    keys: config
558                        .keys
559                        .map(|key| key.as_ref().to_vec()) // TODO: to_vec() overhead
560                        .collect::<Vec<_>>()
561                        .into_iter(),
562                },
563                timeout,
564                result: tx,
565            })
566            .await
567            .unwrap();
568
569        let result = rx.await.unwrap();
570        self.metrics
571            .storage_proof_requests
572            .observe(result.is_ok(), self.platform.now() - when_started);
573        result
574    }
575
576    /// Sends a call proof request to the given peer.
577    ///
578    /// See also [`NetworkServiceChain::call_proof_request`].
579    // TODO: more docs
580    pub async fn call_proof_request(
581        self: Arc<Self>,
582        target: PeerId, // TODO: takes by value because of futures longevity issue
583        config: codec::CallProofRequestConfig<'_, impl Iterator<Item = impl AsRef<[u8]>>>,
584        timeout: Duration,
585    ) -> Result<EncodedMerkleProof, CallProofRequestError> {
586        let when_started = self.platform.now();
587        let (tx, rx) = oneshot::channel();
588
589        self.messages_tx
590            .send(ToBackgroundChain::StartCallProofRequest {
591                target: target.clone(),
592                config: codec::CallProofRequestConfig {
593                    block_hash: config.block_hash,
594                    method: config.method.into_owned().into(),
595                    parameter_vectored: config
596                        .parameter_vectored
597                        .map(|v| v.as_ref().to_vec()) // TODO: to_vec() overhead
598                        .collect::<Vec<_>>()
599                        .into_iter(),
600                },
601                timeout,
602                result: tx,
603            })
604            .await
605            .unwrap();
606
607        let result = rx.await.unwrap();
608        self.metrics
609            .call_proof_requests
610            .observe(result.is_ok(), self.platform.now() - when_started);
611        result
612    }
613
614    /// Sends a child storage proof request to the given peer.
615    pub async fn child_storage_proof_request(
616        self: Arc<Self>,
617        target: PeerId,
618        config: codec::ChildStorageProofRequestConfig<
619            impl AsRef<[u8]> + Clone,
620            impl Iterator<Item = impl AsRef<[u8]> + Clone>,
621        >,
622        timeout: Duration,
623    ) -> Result<service::EncodedMerkleProof, ChildStorageProofRequestError> {
624        let when_started = self.platform.now();
625        let (tx, rx) = oneshot::channel();
626
627        self.messages_tx
628            .send(ToBackgroundChain::StartChildStorageProofRequest {
629                target: target.clone(),
630                config: ChildStorageProofRequestConfigOwned {
631                    block_hash: config.block_hash,
632                    child_trie: config.child_trie.as_ref().to_vec(),
633                    keys: config
634                        .keys
635                        .map(|key| key.as_ref().to_vec())
636                        .collect::<Vec<_>>(),
637                },
638                timeout,
639                result: tx,
640            })
641            .await
642            .unwrap();
643
644        let result = rx.await.unwrap();
645        self.metrics
646            .child_storage_proof_requests
647            .observe(result.is_ok(), self.platform.now() - when_started);
648        result
649    }
650
651    /// Announces transaction to the peers we are connected to.
652    ///
653    /// Returns a list of peers that we have sent the transaction to. Can return an empty `Vec`
654    /// if we didn't send the transaction to any peer.
655    ///
656    /// Note that the remote doesn't confirm that it has received the transaction. Because
657    /// networking is inherently unreliable, successfully sending a transaction to a peer doesn't
658    /// necessarily mean that the remote has received it. In practice, however, the likelihood of
659    /// a transaction not being received are extremely low. This can be considered as known flaw.
660    pub async fn announce_transaction(self: Arc<Self>, transaction: &[u8]) -> Vec<PeerId> {
661        let (tx, rx) = oneshot::channel();
662
663        self.messages_tx
664            .send(ToBackgroundChain::AnnounceTransaction {
665                transaction: transaction.to_vec(), // TODO: ovheread
666                result: tx,
667            })
668            .await
669            .unwrap();
670
671        rx.await.unwrap()
672    }
673
674    /// See [`service::ChainNetwork::gossip_send_block_announce`].
675    pub async fn send_block_announce(
676        self: Arc<Self>,
677        target: &PeerId,
678        scale_encoded_header: &[u8],
679        is_best: bool,
680    ) -> Result<(), QueueNotificationError> {
681        let (tx, rx) = oneshot::channel();
682
683        self.messages_tx
684            .send(ToBackgroundChain::SendBlockAnnounce {
685                target: target.clone(),                              // TODO: overhead
686                scale_encoded_header: scale_encoded_header.to_vec(), // TODO: overhead
687                is_best,
688                result: tx,
689            })
690            .await
691            .unwrap();
692
693        rx.await.unwrap()
694    }
695
696    /// Send Bitswap message to the given peer.
697    pub async fn send_bitswap_message(
698        &self,
699        target: PeerId,
700        message: Vec<u8>,
701    ) -> Result<(), SendBitswapMessageError> {
702        let (tx, rx) = oneshot::channel();
703
704        self.messages_tx
705            .send(ToBackgroundChain::SendBitswapMessage {
706                target,
707                message,
708                result: tx,
709            })
710            .await
711            .unwrap();
712
713        rx.await.unwrap()
714    }
715
716    /// Broadcast Bitswap message to all [`service::ChainNetwork::established_bitswap_desired`]
717    /// peers.
718    ///
719    /// Returns the peers message was broadcast to or an error if the message cannot be sent
720    /// to at least one peer.
721    // TODO: better use a dedicated error type instead of reusing a lower-level
722    // `SendBitswapMessageErorr`.
723    pub async fn broadcast_bitswap_message(
724        &self,
725        message: Vec<u8>,
726    ) -> Result<Vec<PeerId>, SendBitswapMessageError> {
727        let (tx, rx) = oneshot::channel();
728
729        self.messages_tx
730            .send(ToBackgroundChain::BroadcastBitswapMessage {
731                message,
732                result: tx,
733            })
734            .await
735            .unwrap();
736
737        rx.await.unwrap()
738    }
739
740    /// Broadcast a statement notification to all gossip-connected peers.
741    pub async fn broadcast_statement(
742        self: Arc<Self>,
743        statement: Vec<u8>,
744    ) -> BroadcastStatementResult {
745        let (tx, rx) = oneshot::channel();
746
747        self.messages_tx
748            .send(ToBackgroundChain::BroadcastStatement {
749                statement,
750                result: tx,
751            })
752            .await
753            .unwrap();
754
755        rx.await.unwrap()
756    }
757
758    pub async fn update_topic_affinity(&self, filter: AffinityFilter) {
759        self.messages_tx
760            .send(ToBackgroundChain::UpdateTopicAffinity { filter })
761            .await
762            .unwrap();
763    }
764
765    /// Marks the given peers as belonging to the given chain, and adds some addresses to these
766    /// peers to the address book.
767    ///
768    /// The `important_nodes` parameter indicates whether these nodes are considered note-worthy
769    /// and should have additional logging.
770    pub async fn discover(
771        &self,
772        list: impl IntoIterator<Item = (PeerId, impl IntoIterator<Item = Multiaddr>)>,
773        important_nodes: bool,
774    ) {
775        self.messages_tx
776            .send(ToBackgroundChain::Discover {
777                // TODO: overhead
778                list: list
779                    .into_iter()
780                    .map(|(peer_id, addrs)| {
781                        (peer_id, addrs.into_iter().collect::<Vec<_>>().into_iter())
782                    })
783                    .collect::<Vec<_>>()
784                    .into_iter(),
785                important_nodes,
786            })
787            .await
788            .unwrap();
789    }
790
791    /// Returns a list of nodes (their [`PeerId`] and multiaddresses) that we know are part of
792    /// the network.
793    ///
794    /// Nodes that are discovered might disappear over time. In other words, there is no guarantee
795    /// that a node that has been added through [`NetworkServiceChain::discover`] will later be
796    /// returned by [`NetworkServiceChain::discovered_nodes`].
797    pub async fn discovered_nodes(
798        &self,
799    ) -> impl Iterator<Item = (PeerId, impl Iterator<Item = Multiaddr>)> {
800        let (tx, rx) = oneshot::channel();
801
802        self.messages_tx
803            .send(ToBackgroundChain::DiscoveredNodes { result: tx })
804            .await
805            .unwrap();
806
807        rx.await
808            .unwrap()
809            .into_iter()
810            .map(|(peer_id, addrs)| (peer_id, addrs.into_iter()))
811    }
812
813    /// Returns an iterator to the list of [`PeerId`]s that we have an established connection
814    /// with.
815    pub async fn peers_list(&self) -> impl Iterator<Item = PeerId> {
816        let (tx, rx) = oneshot::channel();
817        self.messages_tx
818            .send(ToBackgroundChain::PeersList { result: tx })
819            .await
820            .unwrap();
821        rx.await.unwrap().into_iter()
822    }
823}
824
825#[derive(Debug, Clone)]
826pub struct BroadcastStatementResult {
827    pub sent: usize,
828    pub total: usize,
829}
830
831/// Event that can happen on the network service.
832#[derive(Debug, Clone)]
833pub enum Event {
834    Connected {
835        peer_id: PeerId,
836        role: Role,
837        best_block_number: u64,
838        best_block_hash: [u8; 32],
839    },
840    Disconnected {
841        peer_id: PeerId,
842    },
843    BlockAnnounce {
844        peer_id: PeerId,
845        announce: service::EncodedBlockAnnounce,
846    },
847    GrandpaNeighborPacket {
848        peer_id: PeerId,
849        finalized_block_height: u64,
850    },
851    /// Received a GrandPa commit message from the network.
852    GrandpaCommitMessage {
853        peer_id: PeerId,
854        message: service::EncodedGrandpaCommitMessage,
855    },
856}
857
858/// Bitswap event that can be generated by the network service. Because Bitswap messages are big
859/// (up to 2 MiB) and can be delivered at high rate, we use a dedicated subscriber to not copy them
860/// to all network service subscribers.
861#[derive(Debug, Clone)]
862pub enum BitswapEvent {
863    BitswapMessage {
864        peer_id: PeerId,
865        message: service::EncodedBitswapMessage,
866    },
867}
868
869/// Statement event that can be generated by the network service.
870#[derive(Debug, Clone)]
871pub enum StatementEvent {
872    /// Received a statement notification from the network.
873    StatementsNotification {
874        peer_id: PeerId,
875        statements: Vec<([u8; 32], codec::Statement)>,
876    },
877}
878
879/// Error returned by [`NetworkServiceChain::blocks_request`].
880#[derive(Debug, derive_more::Display, derive_more::Error)]
881pub enum BlocksRequestError {
882    /// No established connection with the target.
883    NoConnection,
884    /// Error during the request.
885    #[display("{_0}")]
886    Request(service::BlocksRequestError),
887}
888
889/// Error returned by [`NetworkServiceChain::grandpa_warp_sync_request`].
890#[derive(Debug, derive_more::Display, derive_more::Error)]
891pub enum WarpSyncRequestError {
892    /// No established connection with the target.
893    NoConnection,
894    /// Error during the request.
895    #[display("{_0}")]
896    Request(service::GrandpaWarpSyncRequestError),
897}
898
899/// Error returned by [`NetworkServiceChain::storage_proof_request`].
900#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
901pub enum StorageProofRequestError {
902    /// No established connection with the target.
903    NoConnection,
904    /// Storage proof request is too large and can't be sent.
905    RequestTooLarge,
906    /// Error during the request.
907    #[display("{_0}")]
908    Request(service::StorageProofRequestError),
909}
910
911/// Error returned by [`NetworkServiceChain::call_proof_request`].
912#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
913pub enum CallProofRequestError {
914    /// No established connection with the target.
915    NoConnection,
916    /// Call proof request is too large and can't be sent.
917    RequestTooLarge,
918    /// Error during the request.
919    #[display("{_0}")]
920    Request(service::CallProofRequestError),
921}
922
923impl CallProofRequestError {
924    /// Returns `true` if this is caused by networking issues, as opposed to a consensus-related
925    /// issue.
926    pub fn is_network_problem(&self) -> bool {
927        match self {
928            CallProofRequestError::Request(err) => err.is_network_problem(),
929            CallProofRequestError::RequestTooLarge => false,
930            CallProofRequestError::NoConnection => true,
931        }
932    }
933}
934
935/// Error returned by [`NetworkServiceChain::child_storage_proof_request`].
936#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
937pub enum ChildStorageProofRequestError {
938    /// No established connection with the target.
939    NoConnection,
940    /// Child storage proof request is too large and can't be sent.
941    RequestTooLarge,
942    /// Error during the request.
943    #[display("{_0}")]
944    Request(service::StorageProofRequestError),
945}
946
947impl ChildStorageProofRequestError {
948    /// Returns `true` if this is caused by networking issues, as opposed to a consensus-related
949    /// issue.
950    pub fn is_network_problem(&self) -> bool {
951        match self {
952            ChildStorageProofRequestError::Request(err) => err.is_network_problem(),
953            ChildStorageProofRequestError::RequestTooLarge => false,
954            ChildStorageProofRequestError::NoConnection => true,
955        }
956    }
957}
958
959/// Why an address obtained through discovery was discarded. The `reason` label of
960/// the `networkDiscoveryAddressesDroppedTotal` metric.
961// Strum derives are explained on [`crate::metrics::MetricLabel`]; `Display`
962// additionally prints the same string in log messages.
963#[derive(
964    Debug, Clone, Copy, PartialEq, Eq, strum::Display, strum::EnumIter, strum::IntoStaticStr,
965)]
966#[strum(serialize_all = "kebab-case")]
967pub(crate) enum DiscoveredAddressDropReason {
968    /// Address contains a `/p2p/` component that doesn't match the peer it was announced for.
969    PeerIdMismatch,
970    /// Platform doesn't support connecting to this type of address.
971    NotSupported,
972    /// Address failed to parse.
973    Invalid,
974}
975
976/// Owned version of [`codec::ChildStorageProofRequestConfig`] for sending across channel.
977struct ChildStorageProofRequestConfigOwned {
978    block_hash: [u8; 32],
979    child_trie: Vec<u8>,
980    keys: Vec<Vec<u8>>,
981}
982
983enum ToBackground<TPlat: PlatformRef> {
984    AddChain {
985        messages_rx: async_channel::Receiver<ToBackgroundChain>,
986        config: service::ChainConfig<Chain<TPlat>>,
987    },
988}
989
990enum ToBackgroundChain {
991    RemoveChain,
992    Subscribe {
993        sender: async_channel::Sender<Event>,
994    },
995    SubscribeBitswap {
996        sender: async_channel::Sender<BitswapEvent>,
997    },
998    SubscribeStatements {
999        sender: async_channel::Sender<StatementEvent>,
1000    },
1001    DisconnectAndBan {
1002        peer_id: PeerId,
1003        severity: BanSeverity,
1004        reason: BanReason,
1005    },
1006    // TODO: serialize the request before sending over channel
1007    StartBlocksRequest {
1008        target: PeerId, // TODO: takes by value because of future longevity issue
1009        config: codec::BlocksRequestConfig,
1010        timeout: Duration,
1011        result: oneshot::Sender<Result<Vec<codec::BlockData>, BlocksRequestError>>,
1012    },
1013    // TODO: serialize the request before sending over channel
1014    StartWarpSyncRequest {
1015        target: PeerId,
1016        begin_hash: [u8; 32],
1017        timeout: Duration,
1018        result:
1019            oneshot::Sender<Result<service::EncodedGrandpaWarpSyncResponse, WarpSyncRequestError>>,
1020    },
1021    // TODO: serialize the request before sending over channel
1022    StartStorageProofRequest {
1023        target: PeerId,
1024        config: codec::StorageProofRequestConfig<vec::IntoIter<Vec<u8>>>,
1025        timeout: Duration,
1026        result: oneshot::Sender<Result<service::EncodedMerkleProof, StorageProofRequestError>>,
1027    },
1028    // TODO: serialize the request before sending over channel
1029    StartCallProofRequest {
1030        target: PeerId, // TODO: takes by value because of futures longevity issue
1031        config: codec::CallProofRequestConfig<'static, vec::IntoIter<Vec<u8>>>,
1032        timeout: Duration,
1033        result: oneshot::Sender<Result<service::EncodedMerkleProof, CallProofRequestError>>,
1034    },
1035    // TODO: serialize the request before sending over channel
1036    StartChildStorageProofRequest {
1037        target: PeerId,
1038        config: ChildStorageProofRequestConfigOwned,
1039        timeout: Duration,
1040        result: oneshot::Sender<Result<service::EncodedMerkleProof, ChildStorageProofRequestError>>,
1041    },
1042    SetLocalBestBlock {
1043        best_hash: [u8; 32],
1044        best_number: u64,
1045    },
1046    SetLocalGrandpaState {
1047        grandpa_state: service::GrandpaState,
1048    },
1049    AnnounceTransaction {
1050        transaction: Vec<u8>,
1051        result: oneshot::Sender<Vec<PeerId>>,
1052    },
1053    SendBlockAnnounce {
1054        target: PeerId,
1055        scale_encoded_header: Vec<u8>,
1056        is_best: bool,
1057        result: oneshot::Sender<Result<(), QueueNotificationError>>,
1058    },
1059    SendBitswapMessage {
1060        target: PeerId,
1061        message: Vec<u8>,
1062        result: oneshot::Sender<Result<(), SendBitswapMessageError>>,
1063    },
1064    BroadcastBitswapMessage {
1065        message: Vec<u8>,
1066        result: oneshot::Sender<Result<Vec<PeerId>, SendBitswapMessageError>>,
1067    },
1068    BroadcastStatement {
1069        statement: Vec<u8>,
1070        result: oneshot::Sender<BroadcastStatementResult>,
1071    },
1072    UpdateTopicAffinity {
1073        filter: AffinityFilter,
1074    },
1075    Discover {
1076        list: vec::IntoIter<(PeerId, vec::IntoIter<Multiaddr>)>,
1077        important_nodes: bool,
1078    },
1079    DiscoveredNodes {
1080        result: oneshot::Sender<Vec<(PeerId, Vec<Multiaddr>)>>,
1081    },
1082    PeersList {
1083        result: oneshot::Sender<Vec<PeerId>>,
1084    },
1085}
1086
1087struct BackgroundTask<TPlat: PlatformRef> {
1088    /// See [`Config::platform`].
1089    platform: TPlat,
1090
1091    /// Process-wide network metrics. Also accessible through [`NetworkService::metrics`].
1092    metrics: Arc<metrics::NetworkMetrics>,
1093
1094    /// Random number generator.
1095    randomness: rand_chacha::ChaCha20Rng,
1096
1097    /// Value provided through [`Config::identify_agent_version`].
1098    identify_agent_version: String,
1099
1100    /// Channel to send messages to the background task.
1101    tasks_messages_tx:
1102        async_channel::Sender<(service::ConnectionId, service::ConnectionToCoordinator)>,
1103
1104    /// Channel to receive messages destined to the background task.
1105    tasks_messages_rx: Pin<
1106        Box<async_channel::Receiver<(service::ConnectionId, service::ConnectionToCoordinator)>>,
1107    >,
1108
1109    /// Data structure holding the entire state of the networking.
1110    network: service::ChainNetwork<
1111        Chain<TPlat>,
1112        async_channel::Sender<service::CoordinatorToConnection>,
1113        TPlat::Instant,
1114    >,
1115
1116    /// All known peers and their addresses.
1117    peering_strategy: basic_peering_strategy::BasicPeeringStrategy<ChainId, TPlat::Instant>,
1118
1119    /// Bitswap slot assignment strategy.
1120    bitswap_peering_strategy: bitswap_peering_strategy::BitswapPeeringStrategy<TPlat::Instant>,
1121
1122    /// See [`Config::connections_open_pool_size`].
1123    connections_open_pool_size: u32,
1124
1125    /// See [`Config::connections_open_pool_restore_delay`].
1126    connections_open_pool_restore_delay: Duration,
1127
1128    /// Every time a connection is opened, the value in this field is increased by one. After
1129    /// [`BackgroundTask::next_recent_connection_restore`] has yielded, the value is reduced by
1130    /// one.
1131    num_recent_connection_opening: u32,
1132
1133    /// Delay after which [`BackgroundTask::num_recent_connection_opening`] is increased by one.
1134    next_recent_connection_restore: Option<Pin<Box<TPlat::Delay>>>,
1135
1136    /// List of all open gossip links.
1137    // TODO: using this data structure unfortunately means that PeerIds are cloned a lot, maybe some user data in ChainNetwork is better? not sure
1138    open_gossip_links: BTreeMap<(ChainId, PeerId), OpenGossipLinkState>,
1139
1140    /// Chains for which a gossip link has been opened at least once. Used to prefer bootnodes for
1141    /// out slots only until the chain first connects.
1142    chains_ever_gossip_connected: HashSet<ChainId, fnv::FnvBuildHasher>,
1143
1144    /// Connected peers using statement protocol V2, per chain.
1145    v2_statement_peers: HashMap<ChainId, HashSet<PeerId, fnv::FnvBuildHasher>, fnv::FnvBuildHasher>,
1146
1147    /// Current topic affinity filter per chain, sent to V2 peers on connect.
1148    current_affinity_filter: HashMap<ChainId, AffinityFilter, fnv::FnvBuildHasher>,
1149
1150    /// Important nodes per chain (in practice the bootnodes; see [`NetworkServiceChain::discover`]).
1151    /// They get extra logging, and slot preference until the chain first connects.
1152    // TODO: should also detect whenever we fail to open a block announces substream with any of these peers
1153    important_nodes: HashMap<ChainId, HashSet<PeerId, fnv::FnvBuildHasher>, fnv::FnvBuildHasher>,
1154
1155    /// Events about to be sent on the senders of [`BackgroundTask::event_senders`].
1156    ///
1157    /// Network events are only pulled when this queue is empty, keeping it small. A queue is
1158    /// nonetheless necessary, as processing a [`ToBackgroundChain::DisconnectAndBan`] message can
1159    /// generate an event while another event is already waiting to be dispatched.
1160    events_pending_send: VecDeque<(ChainId, Event)>,
1161
1162    /// Bitswap event about to be sent on the senders of [`BackgroundTask::bitswap_event_senders`].
1163    bitswap_event_pending_send: Option<BitswapEvent>,
1164
1165    /// Running count of peers with an open Bitswap substream. Maintained from
1166    /// `service::Event::BitswapConnected` / `BitswapDisconnected`. Used for diagnostic logging
1167    /// only; the authoritative per-peer state lives in
1168    /// [`BackgroundTask::bitswap_peering_strategy`].
1169    bitswap_connected_peers: usize,
1170
1171    /// Sending events through the public API.
1172    ///
1173    /// Contains either senders, or a `Future` that is currently sending an event and will yield
1174    /// the senders back once it is finished.
1175    // TODO: sort by ChainId instead of using a Vec?
1176    event_senders: either::Either<
1177        Vec<(ChainId, async_channel::Sender<Event>)>,
1178        Pin<Box<dyn Future<Output = Vec<(ChainId, async_channel::Sender<Event>)>> + Send>>,
1179    >,
1180
1181    /// Whenever [`NetworkServiceChain::subscribe`] is called, the new sender is added to this list.
1182    /// Once [`BackgroundTask::event_senders`] is ready, we properly initialize these senders.
1183    pending_new_subscriptions: Vec<(ChainId, async_channel::Sender<Event>)>,
1184
1185    /// Sending Bitswap events through the public API. We use separate channels for Bitswap events,
1186    /// because Bitswap messages are big and only few of event subscribers are interested in them.
1187    ///
1188    /// Contains either senders, or a `Future` that is currently sending an event and will yield
1189    /// the senders back once it is finished.
1190    ///
1191    /// Note that compared to `event_senders`, `bitswap_event_senders` are not associated with
1192    /// chains, because Bitswap messages coming from the network do not have the information about
1193    /// what chain they are coming from.
1194    bitswap_event_senders: either::Either<
1195        Vec<async_channel::Sender<BitswapEvent>>,
1196        Pin<Box<dyn Future<Output = Vec<async_channel::Sender<BitswapEvent>>> + Send>>,
1197    >,
1198
1199    /// Whenever [`NetworkServiceChain::subscribe_bitswap`] is called, the new sender is added to
1200    /// this list. Once [`BackgroundTask::bitswap_event_senders`] is ready, we properly initialize
1201    /// these senders.
1202    pending_new_bitswap_subscriptions: Vec<async_channel::Sender<BitswapEvent>>,
1203
1204    /// Statement event about to be sent on the senders of
1205    /// [`BackgroundTask::statement_event_senders`].
1206    statement_event_pending_send: Option<(ChainId, StatementEvent)>,
1207
1208    /// Sending statement events through the public API.
1209    ///
1210    /// Contains either senders, or a `Future` that is currently sending an event and will yield
1211    /// the senders back once it is finished.
1212    statement_event_senders: either::Either<
1213        Vec<(ChainId, async_channel::Sender<StatementEvent>)>,
1214        Pin<Box<dyn Future<Output = Vec<(ChainId, async_channel::Sender<StatementEvent>)>> + Send>>,
1215    >,
1216
1217    /// Whenever [`NetworkServiceChain::subscribe_statements`] is called, the new sender is added to
1218    /// this list. Once [`BackgroundTask::statement_event_senders`] is ready, we properly initialize
1219    /// these senders.
1220    pending_new_statement_subscriptions: Vec<(ChainId, async_channel::Sender<StatementEvent>)>,
1221
1222    main_messages_rx: Pin<Box<async_channel::Receiver<ToBackground<TPlat>>>>,
1223
1224    messages_rx:
1225        stream::SelectAll<Pin<Box<dyn stream::Stream<Item = (ChainId, ToBackgroundChain)> + Send>>>,
1226
1227    blocks_requests: HashMap<
1228        service::SubstreamId,
1229        oneshot::Sender<Result<Vec<codec::BlockData>, BlocksRequestError>>,
1230        fnv::FnvBuildHasher,
1231    >,
1232
1233    grandpa_warp_sync_requests: HashMap<
1234        service::SubstreamId,
1235        oneshot::Sender<Result<service::EncodedGrandpaWarpSyncResponse, WarpSyncRequestError>>,
1236        fnv::FnvBuildHasher,
1237    >,
1238
1239    storage_proof_requests: HashMap<
1240        service::SubstreamId,
1241        oneshot::Sender<Result<service::EncodedMerkleProof, StorageProofRequestError>>,
1242        fnv::FnvBuildHasher,
1243    >,
1244
1245    call_proof_requests: HashMap<
1246        service::SubstreamId,
1247        oneshot::Sender<Result<service::EncodedMerkleProof, CallProofRequestError>>,
1248        fnv::FnvBuildHasher,
1249    >,
1250
1251    child_storage_proof_requests: HashMap<
1252        service::SubstreamId,
1253        oneshot::Sender<Result<service::EncodedMerkleProof, ChildStorageProofRequestError>>,
1254        fnv::FnvBuildHasher,
1255    >,
1256
1257    /// All chains, indexed by the value of [`Chain::next_discovery_when`].
1258    chains_by_next_discovery: BTreeMap<(TPlat::Instant, ChainId), Pin<Box<TPlat::Delay>>>,
1259}
1260
1261struct Chain<TPlat: PlatformRef> {
1262    log_name: String,
1263
1264    // TODO: this field is a hack due to the fact that `add_chain` can't be `async`; should eventually be fixed after a lib.rs refactor
1265    num_references: NonZero<usize>,
1266
1267    /// See [`ConfigChain::block_number_bytes`].
1268    // TODO: redundant with ChainNetwork? since we might not need to know this in the future i'm reluctant to add a getter to ChainNetwork
1269    block_number_bytes: usize,
1270
1271    /// See [`ConfigChain::num_out_slots`].
1272    num_out_slots: usize,
1273
1274    /// See [`ConfigChain::metrics`].
1275    metrics: Arc<metrics::ChainMetrics>,
1276
1277    /// When the next discovery should be started for this chain.
1278    next_discovery_when: TPlat::Instant,
1279
1280    /// After [`Chain::next_discovery_when`] is reached, the following discovery happens after
1281    /// the given duration.
1282    next_discovery_period: Duration,
1283}
1284
1285#[derive(Clone)]
1286struct OpenGossipLinkState {
1287    role: Role,
1288    best_block_number: u64,
1289    best_block_hash: [u8; 32],
1290    /// `None` if unknown.
1291    finalized_block_height: Option<u64>,
1292}
1293
1294async fn background_task<TPlat: PlatformRef>(mut task: BackgroundTask<TPlat>) {
1295    loop {
1296        // Yield at every loop in order to provide better tasks granularity.
1297        futures_lite::future::yield_now().await;
1298
1299        enum WakeUpReason<TPlat: PlatformRef> {
1300            ForegroundClosed,
1301            Message(ToBackground<TPlat>),
1302            MessageForChain(ChainId, ToBackgroundChain),
1303            NetworkEvent(service::Event<async_channel::Sender<service::CoordinatorToConnection>>),
1304            CanAssignSlot(PeerId, ChainId),
1305            CanAssignBitswapSlot(PeerId),
1306            NextRecentConnectionRestore,
1307            CanStartConnect(PeerId),
1308            CanOpenGossip(PeerId, ChainId, service::GossipKind),
1309            CanOpenBitswap(PeerId),
1310            MessageFromConnection {
1311                connection_id: service::ConnectionId,
1312                message: service::ConnectionToCoordinator,
1313            },
1314            MessageToConnection {
1315                connection_id: service::ConnectionId,
1316                message: service::CoordinatorToConnection,
1317            },
1318            EventSendersReady,
1319            BitswapEventSendersReady,
1320            StatementEventSendersReady,
1321            StartDiscovery(ChainId),
1322        }
1323
1324        let wake_up_reason = {
1325            let message_received = async {
1326                task.main_messages_rx
1327                    .next()
1328                    .await
1329                    .map_or(WakeUpReason::ForegroundClosed, WakeUpReason::Message)
1330            };
1331            let message_for_chain_received = async {
1332                // Note that when the last entry of `messages_rx` yields `None`, `messages_rx`
1333                // itself will yield `None`. For this reason, we can't use
1334                // `task.messages_rx.is_empty()` to determine whether `messages_rx` will
1335                // yield `None`.
1336                let Some((chain_id, message)) = task.messages_rx.next().await else {
1337                    future::pending().await
1338                };
1339                WakeUpReason::MessageForChain(chain_id, message)
1340            };
1341            let message_from_task_received = async {
1342                let (connection_id, message) = task.tasks_messages_rx.next().await.unwrap();
1343                WakeUpReason::MessageFromConnection {
1344                    connection_id,
1345                    message,
1346                }
1347            };
1348            let service_event = async {
1349                if let Some(event) = (task.events_pending_send.is_empty()
1350                    && task.bitswap_event_pending_send.is_none()
1351                    && task.statement_event_pending_send.is_none()
1352                    && task.pending_new_subscriptions.is_empty()
1353                    && task.pending_new_bitswap_subscriptions.is_empty()
1354                    && task.pending_new_statement_subscriptions.is_empty())
1355                .then(|| task.network.next_event())
1356                .flatten()
1357                {
1358                    WakeUpReason::NetworkEvent(event)
1359                } else if let Some(start_connect) = {
1360                    let x = (task.num_recent_connection_opening < task.connections_open_pool_size)
1361                        .then(|| {
1362                            task.network
1363                                .unconnected_desired()
1364                                .choose(&mut task.randomness)
1365                                .cloned()
1366                        })
1367                        .flatten();
1368                    x
1369                } {
1370                    WakeUpReason::CanStartConnect(start_connect)
1371                } else if let Some((peer_id, chain_id, kind)) = {
1372                    let x = task
1373                        .network
1374                        .connected_unopened_gossip_desired()
1375                        .choose(&mut task.randomness)
1376                        .map(|(peer_id, chain_id, kind)| (peer_id.clone(), chain_id, kind));
1377                    x
1378                } {
1379                    WakeUpReason::CanOpenGossip(peer_id, chain_id, kind)
1380                } else if let Some(peer_id) = {
1381                    let x = task
1382                        .network
1383                        .connected_unopened_bitswap_desired()
1384                        .choose(&mut task.randomness)
1385                        .cloned();
1386                    x
1387                } {
1388                    WakeUpReason::CanOpenBitswap(peer_id)
1389                } else if let Some((connection_id, message)) =
1390                    task.network.pull_message_to_connection()
1391                {
1392                    WakeUpReason::MessageToConnection {
1393                        connection_id,
1394                        message,
1395                    }
1396                } else {
1397                    'search: loop {
1398                        let mut earlier_unban = None;
1399
1400                        for chain_id in task.network.chains().collect::<Vec<_>>() {
1401                            if task.network.gossip_desired_num(
1402                                chain_id,
1403                                service::GossipKind::ConsensusTransactions,
1404                            ) >= task.network[chain_id].num_out_slots
1405                            {
1406                                continue;
1407                            }
1408
1409                            let now = task.platform.now();
1410
1411                            // Until the chain first connects, prefer slots for important nodes
1412                            // (the bootnodes); otherwise use the general pool.
1413                            if !task.chains_ever_gossip_connected.contains(&chain_id) {
1414                                if let basic_peering_strategy::AssignablePeer::Assignable(peer_id) =
1415                                    task.peering_strategy.pick_assignable_peer_filtered(
1416                                        &chain_id,
1417                                        &now,
1418                                        |peer_id| {
1419                                            task.important_nodes
1420                                                .get(&chain_id)
1421                                                .map_or(false, |nodes| nodes.contains(peer_id))
1422                                        },
1423                                    )
1424                                {
1425                                    break 'search WakeUpReason::CanAssignSlot(
1426                                        peer_id.clone(),
1427                                        chain_id,
1428                                    );
1429                                }
1430                            }
1431
1432                            match task.peering_strategy.pick_assignable_peer(&chain_id, &now) {
1433                                basic_peering_strategy::AssignablePeer::Assignable(peer_id) => {
1434                                    break 'search WakeUpReason::CanAssignSlot(
1435                                        peer_id.clone(),
1436                                        chain_id,
1437                                    );
1438                                }
1439                                basic_peering_strategy::AssignablePeer::AllPeersBanned {
1440                                    next_unban,
1441                                } => {
1442                                    if earlier_unban.as_ref().map_or(true, |b| b > next_unban) {
1443                                        earlier_unban = Some(next_unban.clone());
1444                                    }
1445                                }
1446                                basic_peering_strategy::AssignablePeer::NoPeer => continue,
1447                            }
1448                        }
1449
1450                        match task
1451                            .bitswap_peering_strategy
1452                            .pick_assignable_peer(&task.platform.now())
1453                        {
1454                            bitswap_peering_strategy::AssignablePeer::Assignable(peer_id) => {
1455                                break 'search WakeUpReason::CanAssignBitswapSlot(peer_id.clone());
1456                            }
1457                            bitswap_peering_strategy::AssignablePeer::AllPeersBanned {
1458                                next_unban,
1459                            } => {
1460                                if earlier_unban.as_ref().map_or(true, |b| b > next_unban) {
1461                                    earlier_unban = Some(next_unban.clone());
1462                                }
1463                            }
1464                            bitswap_peering_strategy::AssignablePeer::NoPeer => {}
1465                        }
1466
1467                        if let Some(earlier_unban) = earlier_unban {
1468                            task.platform.sleep_until(earlier_unban).await;
1469                        } else {
1470                            future::pending::<()>().await;
1471                        }
1472                    }
1473                }
1474            };
1475            let next_recent_connection_restore = async {
1476                if task.num_recent_connection_opening != 0
1477                    && task.next_recent_connection_restore.is_none()
1478                {
1479                    task.next_recent_connection_restore = Some(Box::pin(
1480                        task.platform
1481                            .sleep(task.connections_open_pool_restore_delay),
1482                    ));
1483                }
1484                if let Some(delay) = task.next_recent_connection_restore.as_mut() {
1485                    delay.await;
1486                    task.next_recent_connection_restore = None;
1487                    WakeUpReason::NextRecentConnectionRestore
1488                } else {
1489                    future::pending().await
1490                }
1491            };
1492            let finished_sending_event = async {
1493                if let either::Right(event_sending_future) = &mut task.event_senders {
1494                    let event_senders = event_sending_future.await;
1495                    task.event_senders = either::Left(event_senders);
1496                    WakeUpReason::EventSendersReady
1497                } else if !task.events_pending_send.is_empty()
1498                    || !task.pending_new_subscriptions.is_empty()
1499                {
1500                    WakeUpReason::EventSendersReady
1501                } else {
1502                    future::pending().await
1503                }
1504            };
1505            let finished_sending_bitswap_event = async {
1506                if let either::Right(bitswap_event_sending_future) = &mut task.bitswap_event_senders
1507                {
1508                    let bitswap_event_senders = bitswap_event_sending_future.await;
1509                    task.bitswap_event_senders = either::Left(bitswap_event_senders);
1510                    WakeUpReason::BitswapEventSendersReady
1511                } else if task.bitswap_event_pending_send.is_some()
1512                    || !task.pending_new_bitswap_subscriptions.is_empty()
1513                {
1514                    WakeUpReason::BitswapEventSendersReady
1515                } else {
1516                    future::pending().await
1517                }
1518            };
1519            let finished_sending_statement_event = async {
1520                if let either::Right(statement_event_sending_future) =
1521                    &mut task.statement_event_senders
1522                {
1523                    let statement_event_senders = statement_event_sending_future.await;
1524                    task.statement_event_senders = either::Left(statement_event_senders);
1525                    WakeUpReason::StatementEventSendersReady
1526                } else if task.statement_event_pending_send.is_some()
1527                    || !task.pending_new_statement_subscriptions.is_empty()
1528                {
1529                    WakeUpReason::StatementEventSendersReady
1530                } else {
1531                    future::pending().await
1532                }
1533            };
1534            let start_discovery = async {
1535                let Some(mut next_discovery) = task.chains_by_next_discovery.first_entry() else {
1536                    future::pending().await
1537                };
1538                next_discovery.get_mut().await;
1539                let ((_, chain_id), _) = next_discovery.remove_entry();
1540                WakeUpReason::StartDiscovery(chain_id)
1541            };
1542
1543            message_for_chain_received
1544                .or(message_received)
1545                .or(message_from_task_received)
1546                .or(service_event)
1547                .or(next_recent_connection_restore)
1548                .or(finished_sending_event)
1549                .or(finished_sending_bitswap_event)
1550                .or(finished_sending_statement_event)
1551                .or(start_discovery)
1552                .await
1553        };
1554
1555        match wake_up_reason {
1556            WakeUpReason::ForegroundClosed => {
1557                // End the task.
1558                return;
1559            }
1560            WakeUpReason::Message(ToBackground::AddChain {
1561                messages_rx,
1562                config,
1563            }) => {
1564                // TODO: this is not a completely clean way of handling duplicate chains, because the existing chain might have a different best block and role and all ; also, multiple sync services will call set_best_block and set_finalized_block
1565                let chain_id = match task.network.add_chain(config) {
1566                    Ok(id) => id,
1567                    Err(service::AddChainError::Duplicate { existing_identical }) => {
1568                        task.network[existing_identical].num_references = task.network
1569                            [existing_identical]
1570                            .num_references
1571                            .checked_add(1)
1572                            .unwrap();
1573                        existing_identical
1574                    }
1575                };
1576
1577                task.chains_by_next_discovery.insert(
1578                    (task.network[chain_id].next_discovery_when.clone(), chain_id),
1579                    Box::pin(
1580                        task.platform
1581                            .sleep_until(task.network[chain_id].next_discovery_when.clone()),
1582                    ),
1583                );
1584
1585                task.messages_rx
1586                    .push(Box::pin(
1587                        messages_rx
1588                            .map(move |msg| (chain_id, msg))
1589                            .chain(stream::once(future::ready((
1590                                chain_id,
1591                                ToBackgroundChain::RemoveChain,
1592                            )))),
1593                    ) as Pin<Box<_>>);
1594
1595                log!(
1596                    &task.platform,
1597                    Debug,
1598                    "network",
1599                    "chain-added",
1600                    id = task.network[chain_id].log_name
1601                );
1602            }
1603            WakeUpReason::EventSendersReady => {
1604                // Dispatch the pending event, if any, to the various senders.
1605
1606                // We made sure that the senders were ready before generating an event.
1607                let either::Left(event_senders) = &mut task.event_senders else {
1608                    unreachable!()
1609                };
1610
1611                if let Some((event_to_dispatch_chain_id, event_to_dispatch)) =
1612                    task.events_pending_send.pop_front()
1613                {
1614                    let mut event_senders = mem::take(event_senders);
1615                    task.event_senders = either::Right(Box::pin(async move {
1616                        // Elements in `event_senders` are removed one by one and inserted
1617                        // back if the channel is still open.
1618                        for index in (0..event_senders.len()).rev() {
1619                            let (event_sender_chain_id, event_sender) =
1620                                event_senders.swap_remove(index);
1621                            if event_sender_chain_id == event_to_dispatch_chain_id {
1622                                if event_sender.send(event_to_dispatch.clone()).await.is_err() {
1623                                    continue;
1624                                }
1625                            }
1626                            event_senders.push((event_sender_chain_id, event_sender));
1627                        }
1628                        event_senders
1629                    }));
1630                } else if !task.pending_new_subscriptions.is_empty() {
1631                    let pending_new_subscriptions = mem::take(&mut task.pending_new_subscriptions);
1632                    let mut event_senders = mem::take(event_senders);
1633                    // TODO: cloning :-/
1634                    let open_gossip_links = task.open_gossip_links.clone();
1635                    task.event_senders = either::Right(Box::pin(async move {
1636                        for (chain_id, new_subscription) in pending_new_subscriptions {
1637                            for ((link_chain_id, peer_id), state) in &open_gossip_links {
1638                                // TODO: optimize? this is O(n) by chain
1639                                if *link_chain_id != chain_id {
1640                                    continue;
1641                                }
1642
1643                                let _ = new_subscription
1644                                    .send(Event::Connected {
1645                                        peer_id: peer_id.clone(),
1646                                        role: state.role,
1647                                        best_block_number: state.best_block_number,
1648                                        best_block_hash: state.best_block_hash,
1649                                    })
1650                                    .await;
1651
1652                                if let Some(finalized_block_height) = state.finalized_block_height {
1653                                    let _ = new_subscription
1654                                        .send(Event::GrandpaNeighborPacket {
1655                                            peer_id: peer_id.clone(),
1656                                            finalized_block_height,
1657                                        })
1658                                        .await;
1659                                }
1660                            }
1661
1662                            event_senders.push((chain_id, new_subscription));
1663                        }
1664
1665                        event_senders
1666                    }));
1667                }
1668            }
1669            WakeUpReason::BitswapEventSendersReady => {
1670                // We made sure that the senders were ready before generating an event.
1671                let either::Left(bitswap_event_senders) = &mut task.bitswap_event_senders else {
1672                    unreachable!()
1673                };
1674
1675                if let Some(event_to_dispatch) = task.bitswap_event_pending_send.take() {
1676                    let mut bitswap_event_senders = mem::take(bitswap_event_senders);
1677                    task.bitswap_event_senders = either::Right(Box::pin(async move {
1678                        // Elements in `bitswap_event_senders` are removed one by one and
1679                        // inserted back if the channel is still open.
1680                        for index in (0..bitswap_event_senders.len()).rev() {
1681                            let event_sender = bitswap_event_senders.swap_remove(index);
1682                            if event_sender.send(event_to_dispatch.clone()).await.is_err() {
1683                                continue;
1684                            }
1685                            bitswap_event_senders.push(event_sender);
1686                        }
1687                        bitswap_event_senders
1688                    }));
1689                } else if !task.pending_new_bitswap_subscriptions.is_empty() {
1690                    bitswap_event_senders.append(&mut task.pending_new_bitswap_subscriptions);
1691                }
1692            }
1693            WakeUpReason::StatementEventSendersReady => {
1694                // We made sure that the senders were ready before generating an event.
1695                let either::Left(statement_event_senders) = &mut task.statement_event_senders
1696                else {
1697                    unreachable!()
1698                };
1699
1700                if let Some((event_to_dispatch_chain_id, event_to_dispatch)) =
1701                    task.statement_event_pending_send.take()
1702                {
1703                    let mut statement_event_senders = mem::take(statement_event_senders);
1704                    task.statement_event_senders = either::Right(Box::pin(async move {
1705                        // Elements in `statement_event_senders` are removed one by one and
1706                        // inserted back if the channel is still open.
1707                        for index in (0..statement_event_senders.len()).rev() {
1708                            let (event_sender_chain_id, event_sender) =
1709                                statement_event_senders.swap_remove(index);
1710                            if event_sender_chain_id == event_to_dispatch_chain_id {
1711                                if event_sender.send(event_to_dispatch.clone()).await.is_err() {
1712                                    continue;
1713                                }
1714                            }
1715                            statement_event_senders.push((event_sender_chain_id, event_sender));
1716                        }
1717                        statement_event_senders
1718                    }));
1719                } else if !task.pending_new_statement_subscriptions.is_empty() {
1720                    statement_event_senders.append(&mut task.pending_new_statement_subscriptions);
1721                }
1722            }
1723            WakeUpReason::MessageFromConnection {
1724                connection_id,
1725                message,
1726            } => {
1727                task.network
1728                    .inject_connection_message(connection_id, message);
1729            }
1730            WakeUpReason::MessageForChain(chain_id, ToBackgroundChain::RemoveChain) => {
1731                if let Some(new_ref) =
1732                    NonZero::<usize>::new(task.network[chain_id].num_references.get() - 1)
1733                {
1734                    task.network[chain_id].num_references = new_ref;
1735                    continue;
1736                }
1737
1738                for peer_id in task
1739                    .network
1740                    .gossip_connected_peers(chain_id, service::GossipKind::ConsensusTransactions)
1741                    .cloned()
1742                    .collect::<Vec<_>>()
1743                {
1744                    task.network
1745                        .gossip_close(
1746                            chain_id,
1747                            &peer_id,
1748                            service::GossipKind::ConsensusTransactions,
1749                        )
1750                        .unwrap();
1751
1752                    let _was_in = task.open_gossip_links.remove(&(chain_id, peer_id));
1753                    debug_assert!(_was_in.is_some());
1754                }
1755
1756                for peer_id in task
1757                    .network
1758                    .gossip_connected_peers(chain_id, service::GossipKind::Statement)
1759                    .cloned()
1760                    .collect::<Vec<_>>()
1761                {
1762                    task.network
1763                        .gossip_close(chain_id, &peer_id, service::GossipKind::Statement)
1764                        .unwrap();
1765                }
1766
1767                let _was_in = task
1768                    .chains_by_next_discovery
1769                    .remove(&(task.network[chain_id].next_discovery_when.clone(), chain_id));
1770                debug_assert!(_was_in.is_some());
1771
1772                log!(
1773                    &task.platform,
1774                    Debug,
1775                    "network",
1776                    "chain-removed",
1777                    id = task.network[chain_id].log_name
1778                );
1779                task.v2_statement_peers.remove(&chain_id);
1780                task.current_affinity_filter.remove(&chain_id);
1781                task.important_nodes.remove(&chain_id);
1782                task.chains_ever_gossip_connected.remove(&chain_id);
1783                task.network.remove_chain(chain_id).unwrap();
1784                task.peering_strategy.remove_chain_peers(&chain_id);
1785            }
1786            WakeUpReason::MessageForChain(chain_id, ToBackgroundChain::Subscribe { sender }) => {
1787                task.pending_new_subscriptions.push((chain_id, sender));
1788            }
1789            WakeUpReason::MessageForChain(
1790                _chain_id,
1791                ToBackgroundChain::SubscribeBitswap { sender },
1792            ) => {
1793                task.pending_new_bitswap_subscriptions.push(sender);
1794            }
1795            WakeUpReason::MessageForChain(
1796                chain_id,
1797                ToBackgroundChain::SubscribeStatements { sender },
1798            ) => {
1799                task.pending_new_statement_subscriptions
1800                    .push((chain_id, sender));
1801            }
1802            WakeUpReason::MessageForChain(
1803                chain_id,
1804                ToBackgroundChain::DisconnectAndBan {
1805                    peer_id,
1806                    severity,
1807                    reason,
1808                },
1809            ) => {
1810                let ban_duration = Duration::from_secs(match severity {
1811                    BanSeverity::Low => 10,
1812                    BanSeverity::High => 40,
1813                });
1814
1815                let had_slot = matches!(
1816                    task.peering_strategy.unassign_slot_and_ban(
1817                        &chain_id,
1818                        &peer_id,
1819                        task.platform.now() + ban_duration,
1820                    ),
1821                    basic_peering_strategy::UnassignSlotAndBan::Banned { had_slot: true }
1822                );
1823
1824                if had_slot {
1825                    log!(
1826                        &task.platform,
1827                        Debug,
1828                        "network",
1829                        "slot-unassigned",
1830                        chain = &task.network[chain_id].log_name,
1831                        peer_id,
1832                        ?ban_duration,
1833                        reason = "user-ban",
1834                        user_reason = reason
1835                    );
1836                    task.network.gossip_remove_desired(
1837                        chain_id,
1838                        &peer_id,
1839                        service::GossipKind::ConsensusTransactions,
1840                    );
1841                }
1842
1843                if task.network.gossip_is_connected(
1844                    chain_id,
1845                    &peer_id,
1846                    service::GossipKind::ConsensusTransactions,
1847                ) {
1848                    let _closed_result = task.network.gossip_close(
1849                        chain_id,
1850                        &peer_id,
1851                        service::GossipKind::ConsensusTransactions,
1852                    );
1853                    debug_assert!(_closed_result.is_ok());
1854
1855                    log!(
1856                        &task.platform,
1857                        Debug,
1858                        "network",
1859                        "gossip-closed",
1860                        chain = &task.network[chain_id].log_name,
1861                        peer_id,
1862                    );
1863
1864                    let _was_in = task.open_gossip_links.remove(&(chain_id, peer_id.clone()));
1865                    debug_assert!(_was_in.is_some());
1866                    task.network[chain_id].metrics.gossip_peers_connected.dec();
1867
1868                    // Unlike the network-event handlers below, this message handler can run
1869                    // while another event is already queued, hence the push to a queue.
1870                    task.events_pending_send.push_back((
1871                        chain_id,
1872                        Event::Disconnected {
1873                            peer_id: peer_id.clone(),
1874                        },
1875                    ));
1876                }
1877
1878                // A statement link survives the closing of the block announces link, so it
1879                // needs a close of its own.
1880                task.network.gossip_remove_desired(
1881                    chain_id,
1882                    &peer_id,
1883                    service::GossipKind::Statement,
1884                );
1885                if task
1886                    .network
1887                    .gossip_close(chain_id, &peer_id, service::GossipKind::Statement)
1888                    .is_ok()
1889                {
1890                    log!(
1891                        &task.platform,
1892                        Debug,
1893                        "network",
1894                        "statement-protocol-closed",
1895                        chain = &task.network[chain_id].log_name,
1896                        peer_id,
1897                        reason = "user-ban",
1898                    );
1899                }
1900                if let Some(peers) = task.v2_statement_peers.get_mut(&chain_id) {
1901                    peers.remove(&peer_id);
1902                }
1903            }
1904            WakeUpReason::MessageForChain(
1905                chain_id,
1906                ToBackgroundChain::StartBlocksRequest {
1907                    target,
1908                    config,
1909                    timeout,
1910                    result,
1911                },
1912            ) => {
1913                match &config.start {
1914                    codec::BlocksRequestConfigStart::Hash(hash) => {
1915                        log!(
1916                            &task.platform,
1917                            Debug,
1918                            "network",
1919                            "blocks-request-started",
1920                            chain = task.network[chain_id].log_name, target,
1921                            start = HashDisplay(hash),
1922                            num = config.desired_count.get(),
1923                            descending = ?matches!(config.direction, codec::BlocksRequestDirection::Descending),
1924                            header = ?config.fields.header, body = ?config.fields.body,
1925                            justifications = ?config.fields.justifications
1926                        );
1927                    }
1928                    codec::BlocksRequestConfigStart::Number(number) => {
1929                        log!(
1930                            &task.platform,
1931                            Debug,
1932                            "network",
1933                            "blocks-request-started",
1934                            chain = task.network[chain_id].log_name, target, start = number,
1935                            num = config.desired_count.get(),
1936                            descending = ?matches!(config.direction, codec::BlocksRequestDirection::Descending),
1937                            header = ?config.fields.header, body = ?config.fields.body, justifications = ?config.fields.justifications
1938                        );
1939                    }
1940                }
1941
1942                match task
1943                    .network
1944                    .start_blocks_request(&target, chain_id, config.clone(), timeout)
1945                {
1946                    Ok(substream_id) => {
1947                        task.blocks_requests.insert(substream_id, result);
1948                    }
1949                    Err(service::StartRequestError::NoConnection) => {
1950                        log!(
1951                            &task.platform,
1952                            Debug,
1953                            "network",
1954                            "blocks-request-error",
1955                            chain = task.network[chain_id].log_name,
1956                            target,
1957                            error = "NoConnection"
1958                        );
1959                        let _ = result.send(Err(BlocksRequestError::NoConnection));
1960                    }
1961                }
1962            }
1963            WakeUpReason::MessageForChain(
1964                chain_id,
1965                ToBackgroundChain::StartWarpSyncRequest {
1966                    target,
1967                    begin_hash,
1968                    timeout,
1969                    result,
1970                },
1971            ) => {
1972                log!(
1973                    &task.platform,
1974                    Debug,
1975                    "network",
1976                    "warp-sync-request-started",
1977                    chain = task.network[chain_id].log_name,
1978                    target,
1979                    start = HashDisplay(&begin_hash)
1980                );
1981
1982                match task
1983                    .network
1984                    .start_grandpa_warp_sync_request(&target, chain_id, begin_hash, timeout)
1985                {
1986                    Ok(substream_id) => {
1987                        task.grandpa_warp_sync_requests.insert(substream_id, result);
1988                    }
1989                    Err(service::StartRequestError::NoConnection) => {
1990                        log!(
1991                            &task.platform,
1992                            Debug,
1993                            "network",
1994                            "warp-sync-request-error",
1995                            chain = task.network[chain_id].log_name,
1996                            target,
1997                            error = "NoConnection"
1998                        );
1999                        let _ = result.send(Err(WarpSyncRequestError::NoConnection));
2000                    }
2001                }
2002            }
2003            WakeUpReason::MessageForChain(
2004                chain_id,
2005                ToBackgroundChain::StartStorageProofRequest {
2006                    target,
2007                    config,
2008                    timeout,
2009                    result,
2010                },
2011            ) => {
2012                log!(
2013                    &task.platform,
2014                    Debug,
2015                    "network",
2016                    "storage-proof-request-started",
2017                    chain = task.network[chain_id].log_name,
2018                    target,
2019                    block_hash = HashDisplay(&config.block_hash)
2020                );
2021
2022                match task.network.start_storage_proof_request(
2023                    &target,
2024                    chain_id,
2025                    config.clone(),
2026                    timeout,
2027                ) {
2028                    Ok(substream_id) => {
2029                        task.storage_proof_requests.insert(substream_id, result);
2030                    }
2031                    Err(service::StartRequestMaybeTooLargeError::NoConnection) => {
2032                        log!(
2033                            &task.platform,
2034                            Debug,
2035                            "network",
2036                            "storage-proof-request-error",
2037                            chain = task.network[chain_id].log_name,
2038                            target,
2039                            error = "NoConnection"
2040                        );
2041                        let _ = result.send(Err(StorageProofRequestError::NoConnection));
2042                    }
2043                    Err(service::StartRequestMaybeTooLargeError::RequestTooLarge) => {
2044                        log!(
2045                            &task.platform,
2046                            Debug,
2047                            "network",
2048                            "storage-proof-request-error",
2049                            chain = task.network[chain_id].log_name,
2050                            target,
2051                            error = "RequestTooLarge"
2052                        );
2053                        let _ = result.send(Err(StorageProofRequestError::RequestTooLarge));
2054                    }
2055                };
2056            }
2057            WakeUpReason::MessageForChain(
2058                chain_id,
2059                ToBackgroundChain::StartCallProofRequest {
2060                    target,
2061                    config,
2062                    timeout,
2063                    result,
2064                },
2065            ) => {
2066                log!(
2067                    &task.platform,
2068                    Debug,
2069                    "network",
2070                    "call-proof-request-started",
2071                    chain = task.network[chain_id].log_name,
2072                    target,
2073                    block_hash = HashDisplay(&config.block_hash),
2074                    function = config.method
2075                );
2076                // TODO: log parameter
2077
2078                match task.network.start_call_proof_request(
2079                    &target,
2080                    chain_id,
2081                    config.clone(),
2082                    timeout,
2083                ) {
2084                    Ok(substream_id) => {
2085                        task.call_proof_requests.insert(substream_id, result);
2086                    }
2087                    Err(service::StartRequestMaybeTooLargeError::NoConnection) => {
2088                        log!(
2089                            &task.platform,
2090                            Debug,
2091                            "network",
2092                            "call-proof-request-error",
2093                            chain = task.network[chain_id].log_name,
2094                            target,
2095                            error = "NoConnection"
2096                        );
2097                        let _ = result.send(Err(CallProofRequestError::NoConnection));
2098                    }
2099                    Err(service::StartRequestMaybeTooLargeError::RequestTooLarge) => {
2100                        log!(
2101                            &task.platform,
2102                            Debug,
2103                            "network",
2104                            "call-proof-request-error",
2105                            chain = task.network[chain_id].log_name,
2106                            target,
2107                            error = "RequestTooLarge"
2108                        );
2109                        let _ = result.send(Err(CallProofRequestError::RequestTooLarge));
2110                    }
2111                };
2112            }
2113            WakeUpReason::MessageForChain(
2114                chain_id,
2115                ToBackgroundChain::StartChildStorageProofRequest {
2116                    target,
2117                    config,
2118                    timeout,
2119                    result,
2120                },
2121            ) => {
2122                log!(
2123                    &task.platform,
2124                    Debug,
2125                    "network",
2126                    "child-storage-proof-request-started",
2127                    chain = task.network[chain_id].log_name,
2128                    target,
2129                    block_hash = HashDisplay(&config.block_hash)
2130                );
2131
2132                match task.network.start_child_storage_proof_request(
2133                    &target,
2134                    chain_id,
2135                    codec::ChildStorageProofRequestConfig {
2136                        block_hash: config.block_hash,
2137                        child_trie: &config.child_trie,
2138                        keys: config.keys.iter().map(|k| k.as_slice()),
2139                    },
2140                    timeout,
2141                ) {
2142                    Ok(substream_id) => {
2143                        task.child_storage_proof_requests
2144                            .insert(substream_id, result);
2145                    }
2146                    Err(service::StartRequestMaybeTooLargeError::NoConnection) => {
2147                        log!(
2148                            &task.platform,
2149                            Debug,
2150                            "network",
2151                            "child-storage-proof-request-error",
2152                            chain = task.network[chain_id].log_name,
2153                            target,
2154                            error = "NoConnection"
2155                        );
2156                        let _ = result.send(Err(ChildStorageProofRequestError::NoConnection));
2157                    }
2158                    Err(service::StartRequestMaybeTooLargeError::RequestTooLarge) => {
2159                        log!(
2160                            &task.platform,
2161                            Debug,
2162                            "network",
2163                            "child-storage-proof-request-error",
2164                            chain = task.network[chain_id].log_name,
2165                            target,
2166                            error = "RequestTooLarge"
2167                        );
2168                        let _ = result.send(Err(ChildStorageProofRequestError::RequestTooLarge));
2169                    }
2170                };
2171            }
2172            WakeUpReason::MessageForChain(
2173                chain_id,
2174                ToBackgroundChain::SetLocalBestBlock {
2175                    best_hash,
2176                    best_number,
2177                },
2178            ) => {
2179                task.network
2180                    .set_chain_local_best_block(chain_id, best_hash, best_number);
2181            }
2182            WakeUpReason::MessageForChain(
2183                chain_id,
2184                ToBackgroundChain::SetLocalGrandpaState { grandpa_state },
2185            ) => {
2186                log!(
2187                    &task.platform,
2188                    Debug,
2189                    "network",
2190                    "local-grandpa-state-announced",
2191                    chain = task.network[chain_id].log_name,
2192                    set_id = grandpa_state.set_id,
2193                    commit_finalized_height = grandpa_state.commit_finalized_height,
2194                );
2195
2196                // TODO: log the list of peers we sent the packet to
2197
2198                task.network
2199                    .gossip_broadcast_grandpa_state_and_update(chain_id, grandpa_state);
2200            }
2201            WakeUpReason::MessageForChain(
2202                chain_id,
2203                ToBackgroundChain::AnnounceTransaction {
2204                    transaction,
2205                    result,
2206                },
2207            ) => {
2208                // TODO: keep track of which peer knows about which transaction, and don't send it again
2209
2210                let peers_to_send = task
2211                    .network
2212                    .gossip_connected_peers(chain_id, service::GossipKind::ConsensusTransactions)
2213                    .cloned()
2214                    .collect::<Vec<_>>();
2215
2216                let mut peers_sent = Vec::with_capacity(peers_to_send.len());
2217                let mut peers_queue_full = Vec::with_capacity(peers_to_send.len());
2218                for peer in &peers_to_send {
2219                    match task
2220                        .network
2221                        .gossip_send_transaction(peer, chain_id, &transaction)
2222                    {
2223                        Ok(()) => peers_sent.push(peer.to_base58()),
2224                        Err(QueueNotificationError::QueueFull) => {
2225                            peers_queue_full.push(peer.to_base58())
2226                        }
2227                        Err(QueueNotificationError::NoConnection) => unreachable!(),
2228                    }
2229                }
2230
2231                log!(
2232                    &task.platform,
2233                    Debug,
2234                    "network",
2235                    "transaction-announced",
2236                    chain = task.network[chain_id].log_name,
2237                    transaction =
2238                        hex::encode(blake2_rfc::blake2b::blake2b(32, &[], &transaction).as_bytes()),
2239                    size = transaction.len(),
2240                    peers_sent = peers_sent.join(", "),
2241                    peers_queue_full = peers_queue_full.join(", "),
2242                );
2243
2244                let _ = result.send(peers_to_send);
2245            }
2246            WakeUpReason::MessageForChain(
2247                chain_id,
2248                ToBackgroundChain::SendBlockAnnounce {
2249                    target,
2250                    scale_encoded_header,
2251                    is_best,
2252                    result,
2253                },
2254            ) => {
2255                // TODO: log who the announce was sent to
2256                let _ = result.send(task.network.gossip_send_block_announce(
2257                    &target,
2258                    chain_id,
2259                    &scale_encoded_header,
2260                    is_best,
2261                ));
2262            }
2263            WakeUpReason::MessageForChain(
2264                _chain_id,
2265                ToBackgroundChain::SendBitswapMessage {
2266                    target,
2267                    message,
2268                    result,
2269                },
2270            ) => {
2271                let _ = result.send(task.network.bitswap_send_message(&target, message));
2272            }
2273            WakeUpReason::MessageForChain(
2274                _chain_id,
2275                ToBackgroundChain::BroadcastBitswapMessage { message, result },
2276            ) => {
2277                let peers = task
2278                    .network
2279                    .established_bitswap_desired()
2280                    .cloned()
2281                    .collect::<Vec<_>>();
2282                let results = peers
2283                    .iter()
2284                    .map(|peer| {
2285                        (
2286                            peer,
2287                            task.network.bitswap_send_message(peer, message.clone()),
2288                        )
2289                    })
2290                    .collect::<Vec<_>>(); // we must collect first to send all messages
2291
2292                let succeeded_peers = results
2293                    .iter()
2294                    .filter_map(|(peer, r)| r.is_ok().then(|| (*peer).clone()))
2295                    .collect::<Vec<_>>();
2296
2297                // TODO: introspecting a third-party error type below doesn't seem good.
2298                let r = if !succeeded_peers.is_empty() {
2299                    Ok(succeeded_peers)
2300                } else if results
2301                    .iter()
2302                    .any(|(_peer, r)| matches!(r, Err(SendBitswapMessageError::QueueFull)))
2303                {
2304                    // `QueueFull` has higher priority than `NoConnection` for possible
2305                    // back-pressure in higher level code.
2306                    Err(SendBitswapMessageError::QueueFull)
2307                } else {
2308                    // This is only emitted if all peers fail with `NoConnection` or there is no
2309                    // peers at all.
2310                    Err(SendBitswapMessageError::NoConnection)
2311                };
2312
2313                let _ = result.send(r);
2314            }
2315            WakeUpReason::MessageForChain(
2316                chain_id,
2317                ToBackgroundChain::BroadcastStatement { statement, result },
2318            ) => {
2319                let peers_to_send = task
2320                    .network
2321                    .gossip_connected_peers(chain_id, service::GossipKind::Statement)
2322                    .cloned()
2323                    .collect::<Vec<_>>();
2324
2325                let total = peers_to_send.len();
2326                let mut sent = 0;
2327                for peer in &peers_to_send {
2328                    if task
2329                        .network
2330                        .gossip_send_statement(peer, chain_id, statement.clone())
2331                        .is_ok()
2332                    {
2333                        sent += 1;
2334                    }
2335                }
2336
2337                log!(
2338                    &task.platform,
2339                    Debug,
2340                    "network",
2341                    "statement-broadcast",
2342                    chain = task.network[chain_id].log_name,
2343                    sent,
2344                    total,
2345                );
2346
2347                let _ = result.send(BroadcastStatementResult { sent, total });
2348            }
2349            WakeUpReason::MessageForChain(
2350                chain_id,
2351                ToBackgroundChain::UpdateTopicAffinity { filter },
2352            ) => {
2353                task.current_affinity_filter
2354                    .insert(chain_id, filter.clone());
2355                if let Some(peers) = task.v2_statement_peers.get_mut(&chain_id) {
2356                    let mut to_remove = Vec::new();
2357                    for peer_id in peers.iter() {
2358                        if let Err(
2359                            SendTopicAffinityError::NoConnection
2360                            | SendTopicAffinityError::ProtocolV1,
2361                        ) = task.network.send_topic_affinity(peer_id, chain_id, &filter)
2362                        {
2363                            to_remove.push(peer_id.clone());
2364                        }
2365                    }
2366                    for peer_id in &to_remove {
2367                        peers.remove(peer_id);
2368                    }
2369                }
2370            }
2371            WakeUpReason::MessageForChain(
2372                chain_id,
2373                ToBackgroundChain::Discover {
2374                    list,
2375                    important_nodes,
2376                },
2377            ) => {
2378                for (peer_id, addrs) in list {
2379                    if important_nodes {
2380                        task.important_nodes
2381                            .entry(chain_id)
2382                            .or_default()
2383                            .insert(peer_id.clone());
2384                    }
2385
2386                    // Note that we must call this function before `insert_address`, as documented
2387                    // in `basic_peering_strategy`.
2388                    task.peering_strategy
2389                        .insert_chain_peer(chain_id, peer_id.clone(), 30); // TODO: constant
2390
2391                    for addr in addrs {
2392                        let _ =
2393                            task.peering_strategy
2394                                .insert_address(&peer_id, addr.into_bytes(), 10);
2395                        // TODO: constant
2396                    }
2397                }
2398            }
2399            WakeUpReason::MessageForChain(
2400                chain_id,
2401                ToBackgroundChain::DiscoveredNodes { result },
2402            ) => {
2403                // TODO: consider returning Vec<u8>s for the addresses?
2404                let _ = result.send(
2405                    task.peering_strategy
2406                        .chain_peers_unordered(&chain_id)
2407                        .map(|peer_id| {
2408                            let addrs = task
2409                                .peering_strategy
2410                                .peer_addresses(peer_id)
2411                                .map(|a| Multiaddr::from_bytes(a.to_owned()).unwrap())
2412                                .collect::<Vec<_>>();
2413                            (peer_id.clone(), addrs)
2414                        })
2415                        .collect::<Vec<_>>(),
2416                );
2417            }
2418            WakeUpReason::MessageForChain(chain_id, ToBackgroundChain::PeersList { result }) => {
2419                let _ = result.send(
2420                    task.network
2421                        .gossip_connected_peers(
2422                            chain_id,
2423                            service::GossipKind::ConsensusTransactions,
2424                        )
2425                        .cloned()
2426                        .collect(),
2427                );
2428            }
2429            WakeUpReason::StartDiscovery(chain_id) => {
2430                // Re-insert the chain in `chains_by_next_discovery`.
2431                let chain = &mut task.network[chain_id];
2432                chain.next_discovery_when = task.platform.now() + chain.next_discovery_period;
2433                chain.next_discovery_period =
2434                    cmp::min(chain.next_discovery_period * 2, Duration::from_secs(120));
2435                task.chains_by_next_discovery.insert(
2436                    (chain.next_discovery_when.clone(), chain_id),
2437                    Box::pin(
2438                        task.platform
2439                            .sleep(task.network[chain_id].next_discovery_period),
2440                    ),
2441                );
2442
2443                // Iterative-style discovery: instead of a single FindNode against one peer,
2444                // dispatch up to ALPHA=3 FindNode requests in parallel to distinct peers,
2445                // each with a distinct random target. This gives substantially better DHT
2446                // coverage per discovery round (more peers asked, more diverse keyspace
2447                // walked) without requiring a per-query state machine.
2448                //
2449                // Order of preference for the target peer pool:
2450                //  1. Peers we know speak this chain's Kad protocol (from Identify).
2451                //  2. Peers with an open block-announces gossip substream (best-effort:
2452                //     they're connected and likely speak Kad even if we haven't gotten
2453                //     Identify yet).
2454                const PARALLEL_FIND_NODE_PER_ROUND: usize = 3;
2455
2456                let mut candidates: Vec<PeerId> = task
2457                    .network
2458                    .kademlia_capable_peers(chain_id)
2459                    .cloned()
2460                    .collect();
2461                for p in task
2462                    .network
2463                    .gossip_connected_peers(chain_id, service::GossipKind::ConsensusTransactions)
2464                    .cloned()
2465                {
2466                    if !candidates.contains(&p) {
2467                        candidates.push(p);
2468                    }
2469                }
2470
2471                let started = dispatch_find_node_requests(
2472                    &mut task.network,
2473                    &mut task.randomness,
2474                    chain_id,
2475                    &candidates,
2476                    PARALLEL_FIND_NODE_PER_ROUND,
2477                );
2478
2479                let chain_log_name = &task.network[chain_id].log_name;
2480                for (request_target, requested_peer_id) in &started {
2481                    log!(
2482                        &task.platform,
2483                        Debug,
2484                        "network",
2485                        "discovery-find-node-started",
2486                        chain = chain_log_name,
2487                        request_target,
2488                        requested_peer_id
2489                    );
2490                }
2491                if started.is_empty() {
2492                    log!(
2493                        &task.platform,
2494                        Debug,
2495                        "network",
2496                        "discovery-skipped-no-peer",
2497                        chain = chain_log_name
2498                    );
2499                }
2500            }
2501            WakeUpReason::NetworkEvent(service::Event::HandshakeFinished {
2502                peer_id,
2503                expected_peer_id,
2504                id,
2505            }) => {
2506                let remote_addr =
2507                    Multiaddr::from_bytes(task.network.connection_remote_addr(id)).unwrap(); // TODO: review this unwrap
2508                if let Some(expected_peer_id) = expected_peer_id.as_ref().filter(|p| **p != peer_id)
2509                {
2510                    log!(
2511                        &task.platform,
2512                        Debug,
2513                        "network",
2514                        "handshake-finished-peer-id-mismatch",
2515                        remote_addr,
2516                        expected_peer_id,
2517                        actual_peer_id = peer_id
2518                    );
2519
2520                    let _was_in = task
2521                        .peering_strategy
2522                        .decrease_address_connections_and_remove_if_zero(
2523                            expected_peer_id,
2524                            remote_addr.as_ref(),
2525                        );
2526                    debug_assert!(_was_in.is_ok());
2527                    let _ = task.peering_strategy.increase_address_connections(
2528                        &peer_id,
2529                        remote_addr.into_bytes().to_vec(),
2530                        10,
2531                    );
2532                } else {
2533                    log!(
2534                        &task.platform,
2535                        Debug,
2536                        "network",
2537                        "handshake-finished",
2538                        remote_addr,
2539                        peer_id
2540                    );
2541                }
2542
2543                task.metrics.connections_handshakes_finished.inc();
2544                task.bitswap_peering_strategy
2545                    .increase_peer_connections(&peer_id);
2546            }
2547            WakeUpReason::NetworkEvent(service::Event::PreHandshakeDisconnected {
2548                expected_peer_id: Some(_),
2549                ..
2550            })
2551            | WakeUpReason::NetworkEvent(service::Event::Disconnected { .. }) => {
2552                let (address, peer_id, handshake_finished) = match wake_up_reason {
2553                    WakeUpReason::NetworkEvent(service::Event::PreHandshakeDisconnected {
2554                        address,
2555                        expected_peer_id: Some(peer_id),
2556                        ..
2557                    }) => (address, peer_id, false),
2558                    WakeUpReason::NetworkEvent(service::Event::Disconnected {
2559                        address,
2560                        peer_id,
2561                        ..
2562                    }) => (address, peer_id, true),
2563                    _ => unreachable!(),
2564                };
2565
2566                task.peering_strategy
2567                    .decrease_address_connections(&peer_id, &address)
2568                    .unwrap();
2569                let address = Multiaddr::from_bytes(address).unwrap();
2570                log!(
2571                    &task.platform,
2572                    Debug,
2573                    "network",
2574                    "connection-shutdown",
2575                    peer_id,
2576                    address,
2577                    ?handshake_finished
2578                );
2579                task.metrics.connections_shutdowns.inc();
2580
2581                // Ban the peer in order to avoid trying over and over again the same address(es).
2582                // Even if the handshake was finished, it is possible that the peer simply shuts
2583                // down connections immediately after it has been opened, hence the ban.
2584                // Due to race conditions and peerid mismatches, it is possible that there is
2585                // another existing connection or connection attempt with that same peer. However,
2586                // it is not possible to be sure that we will reach 0 connections or connection
2587                // attempts, and thus we ban the peer every time.
2588                // Pre-handshake failures get a shorter ban: many parallel dials time out
2589                // before any handshake completes, and a long slot-hold there dominates
2590                // peer-discovery latency on restarts.
2591                let ban_duration = if handshake_finished {
2592                    Duration::from_secs(5)
2593                } else {
2594                    Duration::from_secs(2)
2595                };
2596                task.network.gossip_remove_desired_all(
2597                    &peer_id,
2598                    service::GossipKind::ConsensusTransactions,
2599                );
2600                task.network
2601                    .gossip_remove_desired_all(&peer_id, service::GossipKind::Statement);
2602                for (&chain_id, what_happened) in task
2603                    .peering_strategy
2604                    .unassign_slots_and_ban(&peer_id, task.platform.now() + ban_duration)
2605                {
2606                    if matches!(
2607                        what_happened,
2608                        basic_peering_strategy::UnassignSlotsAndBan::Banned { had_slot: true }
2609                    ) {
2610                        log!(
2611                            &task.platform,
2612                            Debug,
2613                            "network",
2614                            "slot-unassigned",
2615                            chain = &task.network[chain_id].log_name,
2616                            peer_id,
2617                            ?ban_duration,
2618                            // TODO: `reason` might be wrong, `handshake_finished` is not checked.
2619                            reason = "pre-handshake-disconnect"
2620                        );
2621                    }
2622                }
2623
2624                if handshake_finished {
2625                    task.network.bitswap_remove_desired(&peer_id);
2626                    let what_happened = task
2627                        .bitswap_peering_strategy
2628                        .unassign_slot_and_ban(&peer_id, task.platform.now() + ban_duration);
2629                    if matches!(
2630                        what_happened,
2631                        bitswap_peering_strategy::UnassignSlotAndBan::Banned { had_slot: true },
2632                    ) {
2633                        log!(
2634                            &task.platform,
2635                            Debug,
2636                            "network",
2637                            "bitswap-slot-unassigned",
2638                            peer_id,
2639                            ?ban_duration,
2640                            reason = "disconnect",
2641                        );
2642                    }
2643                    let _ = task
2644                        .bitswap_peering_strategy
2645                        .decrease_peer_connections(&peer_id);
2646                }
2647            }
2648            WakeUpReason::NetworkEvent(service::Event::PreHandshakeDisconnected {
2649                expected_peer_id: None,
2650                ..
2651            }) => {
2652                // This path can't be reached as we always set an expected peer id when creating
2653                // a connection.
2654                debug_assert!(false);
2655            }
2656            WakeUpReason::NetworkEvent(service::Event::PingOutSuccess {
2657                id,
2658                peer_id,
2659                ping_time,
2660            }) => {
2661                let remote_addr =
2662                    Multiaddr::from_bytes(task.network.connection_remote_addr(id)).unwrap(); // TODO: review this unwrap
2663                log!(
2664                    &task.platform,
2665                    Debug,
2666                    "network",
2667                    "pong",
2668                    peer_id,
2669                    remote_addr,
2670                    ?ping_time
2671                );
2672            }
2673            WakeUpReason::NetworkEvent(service::Event::BlockAnnounce {
2674                chain_id,
2675                peer_id,
2676                announce,
2677            }) => {
2678                log!(
2679                    &task.platform,
2680                    Debug,
2681                    "network",
2682                    "block-announce-received",
2683                    chain = &task.network[chain_id].log_name,
2684                    peer_id,
2685                    block_hash = HashDisplay(&header::hash_from_scale_encoded_header(
2686                        announce.decode().scale_encoded_header
2687                    )),
2688                    is_best = announce.decode().is_best
2689                );
2690
2691                let decoded_announce = announce.decode();
2692                if decoded_announce.is_best {
2693                    let link = task
2694                        .open_gossip_links
2695                        .get_mut(&(chain_id, peer_id.clone()))
2696                        .unwrap();
2697                    if let Ok(decoded) = header::decode(
2698                        decoded_announce.scale_encoded_header,
2699                        task.network[chain_id].block_number_bytes,
2700                    ) {
2701                        link.best_block_hash = header::hash_from_scale_encoded_header(
2702                            decoded_announce.scale_encoded_header,
2703                        );
2704                        link.best_block_number = decoded.number;
2705                    }
2706                }
2707
2708                debug_assert!(task.events_pending_send.is_empty());
2709                task.events_pending_send
2710                    .push_back((chain_id, Event::BlockAnnounce { peer_id, announce }));
2711            }
2712            WakeUpReason::NetworkEvent(service::Event::GossipConnected {
2713                peer_id,
2714                chain_id,
2715                role,
2716                best_number,
2717                best_hash,
2718                kind: service::GossipKind::ConsensusTransactions,
2719            }) => {
2720                log!(
2721                    &task.platform,
2722                    Debug,
2723                    "network",
2724                    "gossip-open-success",
2725                    chain = &task.network[chain_id].log_name,
2726                    peer_id,
2727                    best_number,
2728                    best_hash = HashDisplay(&best_hash)
2729                );
2730
2731                let _prev_value = task.open_gossip_links.insert(
2732                    (chain_id, peer_id.clone()),
2733                    OpenGossipLinkState {
2734                        best_block_number: best_number,
2735                        best_block_hash: best_hash,
2736                        role,
2737                        finalized_block_height: None,
2738                    },
2739                );
2740                debug_assert!(_prev_value.is_none());
2741                task.network[chain_id].metrics.gossip_peers_connected.inc();
2742
2743                task.chains_ever_gossip_connected.insert(chain_id);
2744
2745                debug_assert!(task.events_pending_send.is_empty());
2746                task.events_pending_send.push_back((
2747                    chain_id,
2748                    Event::Connected {
2749                        peer_id,
2750                        role,
2751                        best_block_number: best_number,
2752                        best_block_hash: best_hash,
2753                    },
2754                ));
2755            }
2756            WakeUpReason::NetworkEvent(service::Event::GossipOpenFailed {
2757                peer_id,
2758                chain_id,
2759                error,
2760                kind: service::GossipKind::ConsensusTransactions,
2761            }) => {
2762                log!(
2763                    &task.platform,
2764                    Debug,
2765                    "network",
2766                    "gossip-open-error",
2767                    chain = &task.network[chain_id].log_name,
2768                    peer_id,
2769                    ?error,
2770                );
2771                // Must exceed polkadot-sdk's 5s notification-reject ban; otherwise we retry
2772                // into a still-active remote ban. 0.5s margin covers network delay and
2773                // clock skew between the two sides' ban timers.
2774                let ban_duration = Duration::from_millis(5500);
2775
2776                // Note that peer doesn't necessarily have an out slot, as this event might happen
2777                // as a result of an inbound gossip connection.
2778                let had_slot = if let service::GossipConnectError::GenesisMismatch { .. } = error {
2779                    matches!(
2780                        task.peering_strategy
2781                            .unassign_slot_and_remove_chain_peer(&chain_id, &peer_id),
2782                        basic_peering_strategy::UnassignSlotAndRemoveChainPeer::HadSlot
2783                    )
2784                } else {
2785                    matches!(
2786                        task.peering_strategy.unassign_slot_and_ban(
2787                            &chain_id,
2788                            &peer_id,
2789                            task.platform.now() + ban_duration,
2790                        ),
2791                        basic_peering_strategy::UnassignSlotAndBan::Banned { had_slot: true }
2792                    )
2793                };
2794
2795                if had_slot {
2796                    log!(
2797                        &task.platform,
2798                        Debug,
2799                        "network",
2800                        "slot-unassigned",
2801                        chain = &task.network[chain_id].log_name,
2802                        peer_id,
2803                        ?ban_duration,
2804                        reason = "gossip-open-failed"
2805                    );
2806                    task.network.gossip_remove_desired(
2807                        chain_id,
2808                        &peer_id,
2809                        service::GossipKind::ConsensusTransactions,
2810                    );
2811                }
2812            }
2813            WakeUpReason::NetworkEvent(service::Event::GossipDisconnected {
2814                peer_id,
2815                chain_id,
2816                kind: service::GossipKind::ConsensusTransactions,
2817            }) => {
2818                log!(
2819                    &task.platform,
2820                    Debug,
2821                    "network",
2822                    "gossip-closed",
2823                    chain = &task.network[chain_id].log_name,
2824                    peer_id,
2825                );
2826                let ban_duration = Duration::from_secs(10);
2827
2828                let _was_in = task.open_gossip_links.remove(&(chain_id, peer_id.clone()));
2829                debug_assert!(_was_in.is_some());
2830                task.network[chain_id].metrics.gossip_peers_connected.dec();
2831
2832                // Note that peer doesn't necessarily have an out slot, as this event might happen
2833                // as a result of an inbound gossip connection.
2834                if matches!(
2835                    task.peering_strategy.unassign_slot_and_ban(
2836                        &chain_id,
2837                        &peer_id,
2838                        task.platform.now() + ban_duration,
2839                    ),
2840                    basic_peering_strategy::UnassignSlotAndBan::Banned { had_slot: true }
2841                ) {
2842                    log!(
2843                        &task.platform,
2844                        Debug,
2845                        "network",
2846                        "slot-unassigned",
2847                        chain = &task.network[chain_id].log_name,
2848                        peer_id,
2849                        ?ban_duration,
2850                        reason = "gossip-closed"
2851                    );
2852                    task.network.gossip_remove_desired(
2853                        chain_id,
2854                        &peer_id,
2855                        service::GossipKind::ConsensusTransactions,
2856                    );
2857                }
2858
2859                // The statement substream closes with the block announces substream, unless
2860                // the peer has a statement link of its own.
2861                if !task.network.gossip_is_connected(
2862                    chain_id,
2863                    &peer_id,
2864                    service::GossipKind::Statement,
2865                ) && let Some(peers) = task.v2_statement_peers.get_mut(&chain_id)
2866                {
2867                    peers.remove(&peer_id);
2868                }
2869
2870                debug_assert!(task.events_pending_send.is_empty());
2871                task.events_pending_send
2872                    .push_back((chain_id, Event::Disconnected { peer_id }));
2873            }
2874            WakeUpReason::NetworkEvent(service::Event::BitswapConnected { peer_id }) => {
2875                task.bitswap_connected_peers = task.bitswap_connected_peers.saturating_add(1);
2876                log!(
2877                    &task.platform,
2878                    Debug,
2879                    "network",
2880                    "bitswap-open-success",
2881                    peer_id,
2882                    total = task.bitswap_connected_peers
2883                );
2884            }
2885            WakeUpReason::NetworkEvent(service::Event::BitswapOpenFailed { peer_id, error }) => {
2886                log!(
2887                    &task.platform,
2888                    Debug,
2889                    "network",
2890                    "bitswap-open-error",
2891                    peer_id,
2892                    ?error
2893                );
2894                let ban_duration = if error.is_protocol_not_available() {
2895                    Duration::from_secs(600)
2896                } else {
2897                    Duration::from_secs(15)
2898                };
2899                if matches!(
2900                    task.bitswap_peering_strategy
2901                        .unassign_slot_and_ban(&peer_id, task.platform.now() + ban_duration,),
2902                    bitswap_peering_strategy::UnassignSlotAndBan::Banned { had_slot: true }
2903                ) {
2904                    log!(
2905                        &task.platform,
2906                        Debug,
2907                        "network",
2908                        "bitswap-slot-unassigned",
2909                        peer_id,
2910                        ?ban_duration,
2911                        reason = "bitswap-open-failed"
2912                    );
2913                    task.network.bitswap_remove_desired(&peer_id);
2914                }
2915            }
2916            WakeUpReason::NetworkEvent(service::Event::BitswapMessage { peer_id, message }) => {
2917                log!(
2918                    &task.platform,
2919                    Debug,
2920                    "network",
2921                    "bitswap-message-received",
2922                    peer_id
2923                );
2924                debug_assert!(task.bitswap_event_pending_send.is_none());
2925                task.bitswap_event_pending_send =
2926                    Some(BitswapEvent::BitswapMessage { peer_id, message });
2927            }
2928            WakeUpReason::NetworkEvent(service::Event::BitswapDisconnected { peer_id }) => {
2929                debug_assert!(task.bitswap_connected_peers > 0);
2930                task.bitswap_connected_peers = task.bitswap_connected_peers.saturating_sub(1);
2931                log!(
2932                    &task.platform,
2933                    Debug,
2934                    "network",
2935                    "bitswap-closed",
2936                    peer_id,
2937                    total = task.bitswap_connected_peers
2938                );
2939                let ban_duration = Duration::from_secs(10);
2940                if matches!(
2941                    task.bitswap_peering_strategy
2942                        .unassign_slot_and_ban(&peer_id, task.platform.now() + ban_duration,),
2943                    bitswap_peering_strategy::UnassignSlotAndBan::Banned { had_slot: true }
2944                ) {
2945                    log!(
2946                        &task.platform,
2947                        Debug,
2948                        "network",
2949                        "bitswap-slot-unassigned",
2950                        peer_id,
2951                        ?ban_duration,
2952                        reason = "bitswap-closed"
2953                    );
2954                    task.network.bitswap_remove_desired(&peer_id);
2955                }
2956            }
2957            WakeUpReason::NetworkEvent(service::Event::RequestResult {
2958                substream_id,
2959                peer_id,
2960                chain_id,
2961                response: service::RequestResult::Blocks(response),
2962            }) => {
2963                match &response {
2964                    Ok(blocks) => {
2965                        log!(
2966                            &task.platform,
2967                            Debug,
2968                            "network",
2969                            "blocks-request-success",
2970                            chain = task.network[chain_id].log_name,
2971                            target = peer_id,
2972                            num_blocks = blocks.len(),
2973                            block_data_total_size =
2974                                BytesDisplay(blocks.iter().fold(0, |sum, block| {
2975                                    let block_size = block.header.as_ref().map_or(0, |h| h.len())
2976                                        + block
2977                                            .body
2978                                            .as_ref()
2979                                            .map_or(0, |b| b.iter().fold(0, |s, e| s + e.len()))
2980                                        + block
2981                                            .justifications
2982                                            .as_ref()
2983                                            .into_iter()
2984                                            .flat_map(|l| l.iter())
2985                                            .fold(0, |s, j| s + j.justification.len());
2986                                    sum + u64::try_from(block_size).unwrap()
2987                                }))
2988                        );
2989                    }
2990                    Err(error) => {
2991                        log!(
2992                            &task.platform,
2993                            Debug,
2994                            "network",
2995                            "blocks-request-error",
2996                            chain = task.network[chain_id].log_name,
2997                            target = peer_id,
2998                            ?error
2999                        );
3000                    }
3001                }
3002
3003                match &response {
3004                    Ok(_) => {}
3005                    Err(service::BlocksRequestError::Request(err)) if !err.is_protocol_error() => {}
3006                    Err(err) => {
3007                        log!(
3008                            &task.platform,
3009                            Debug,
3010                            "network",
3011                            format!(
3012                                "Error in block request with {}. This might indicate an \
3013                                incompatibility. Error: {}",
3014                                peer_id, err
3015                            )
3016                        );
3017                    }
3018                }
3019
3020                let _ = task
3021                    .blocks_requests
3022                    .remove(&substream_id)
3023                    .unwrap()
3024                    .send(response.map_err(BlocksRequestError::Request));
3025            }
3026            WakeUpReason::NetworkEvent(service::Event::RequestResult {
3027                substream_id,
3028                peer_id,
3029                chain_id,
3030                response: service::RequestResult::GrandpaWarpSync(response),
3031            }) => {
3032                match &response {
3033                    Ok(response) => {
3034                        // TODO: print total bytes size
3035                        let decoded = response.decode();
3036                        log!(
3037                            &task.platform,
3038                            Debug,
3039                            "network",
3040                            "warp-sync-request-success",
3041                            chain = task.network[chain_id].log_name,
3042                            target = peer_id,
3043                            num_fragments = decoded.fragments.len(),
3044                            is_finished = ?decoded.is_finished,
3045                        );
3046                    }
3047                    Err(error) => {
3048                        log!(
3049                            &task.platform,
3050                            Debug,
3051                            "network",
3052                            "warp-sync-request-error",
3053                            chain = task.network[chain_id].log_name,
3054                            target = peer_id,
3055                            ?error,
3056                        );
3057                    }
3058                }
3059
3060                let _ = task
3061                    .grandpa_warp_sync_requests
3062                    .remove(&substream_id)
3063                    .unwrap()
3064                    .send(response.map_err(WarpSyncRequestError::Request));
3065            }
3066            WakeUpReason::NetworkEvent(service::Event::RequestResult {
3067                substream_id,
3068                peer_id,
3069                chain_id,
3070                response: service::RequestResult::StorageProof(response),
3071            }) => {
3072                match &response {
3073                    Ok(items) => {
3074                        let decoded = items.decode();
3075                        log!(
3076                            &task.platform,
3077                            Debug,
3078                            "network",
3079                            "storage-proof-request-success",
3080                            chain = task.network[chain_id].log_name,
3081                            target = peer_id,
3082                            total_size = BytesDisplay(u64::try_from(decoded.len()).unwrap()),
3083                        );
3084                    }
3085                    Err(error) => {
3086                        log!(
3087                            &task.platform,
3088                            Debug,
3089                            "network",
3090                            "storage-proof-request-error",
3091                            chain = task.network[chain_id].log_name,
3092                            target = peer_id,
3093                            ?error
3094                        );
3095                    }
3096                }
3097
3098                // Both regular storage proof and child storage proof use the same protocol,
3099                // so check both HashMaps for the request.
3100                if let Some(sender) = task.storage_proof_requests.remove(&substream_id) {
3101                    let _ = sender.send(response.map_err(StorageProofRequestError::Request));
3102                } else if let Some(sender) = task.child_storage_proof_requests.remove(&substream_id)
3103                {
3104                    let _ = sender.send(response.map_err(ChildStorageProofRequestError::Request));
3105                } else {
3106                    unreachable!()
3107                }
3108            }
3109            WakeUpReason::NetworkEvent(service::Event::RequestResult {
3110                substream_id,
3111                peer_id,
3112                chain_id,
3113                response: service::RequestResult::CallProof(response),
3114            }) => {
3115                match &response {
3116                    Ok(items) => {
3117                        let decoded = items.decode();
3118                        log!(
3119                            &task.platform,
3120                            Debug,
3121                            "network",
3122                            "call-proof-request-success",
3123                            chain = task.network[chain_id].log_name,
3124                            target = peer_id,
3125                            total_size = BytesDisplay(u64::try_from(decoded.len()).unwrap())
3126                        );
3127                    }
3128                    Err(error) => {
3129                        log!(
3130                            &task.platform,
3131                            Debug,
3132                            "network",
3133                            "call-proof-request-error",
3134                            chain = task.network[chain_id].log_name,
3135                            target = peer_id,
3136                            ?error
3137                        );
3138                    }
3139                }
3140
3141                let _ = task
3142                    .call_proof_requests
3143                    .remove(&substream_id)
3144                    .unwrap()
3145                    .send(response.map_err(CallProofRequestError::Request));
3146            }
3147            WakeUpReason::NetworkEvent(service::Event::RequestResult {
3148                peer_id: requestee_peer_id,
3149                chain_id,
3150                response: service::RequestResult::KademliaFindNode(Ok(nodes)),
3151                ..
3152            }) => {
3153                // Track whether this response taught us anything new. If so, we reset the
3154                // chain's discovery backoff so that the next FindNode round runs at the
3155                // initial 2s interval rather than continuing to back off — Kademlia is
3156                // making progress, walk the DHT eagerly.
3157                let mut any_new_peer = false;
3158                for (peer_id, mut addrs) in nodes {
3159                    // Make sure to not insert too many address for a single peer.
3160                    // While the .
3161                    if addrs.len() >= 10 {
3162                        addrs.truncate(10);
3163                    }
3164
3165                    let mut valid_addrs = Vec::with_capacity(addrs.len());
3166                    for addr in addrs {
3167                        match Multiaddr::from_bytes(addr) {
3168                            Ok(mut a) => {
3169                                if !pop_p2p_if_matches(&mut a, &peer_id) {
3170                                    let reason = DiscoveredAddressDropReason::PeerIdMismatch;
3171                                    log!(
3172                                        &task.platform,
3173                                        Debug,
3174                                        "network",
3175                                        "discovered-address-dropped",
3176                                        chain = &task.network[chain_id].log_name,
3177                                        reason,
3178                                        announced_peer_id = peer_id,
3179                                        addr = &a,
3180                                        obtained_from = requestee_peer_id
3181                                    );
3182                                    task.metrics.discovery_addresses_dropped.inc(reason);
3183                                    continue;
3184                                }
3185                                if platform::address_parse::multiaddr_to_address(&a)
3186                                    .ok()
3187                                    .map_or(false, |addr| {
3188                                        task.platform.supports_connection_type((&addr).into())
3189                                    })
3190                                {
3191                                    valid_addrs.push(a)
3192                                } else {
3193                                    let reason = DiscoveredAddressDropReason::NotSupported;
3194                                    log!(
3195                                        &task.platform,
3196                                        Debug,
3197                                        "network",
3198                                        "discovered-address-dropped",
3199                                        chain = &task.network[chain_id].log_name,
3200                                        reason,
3201                                        peer_id,
3202                                        addr = &a,
3203                                        obtained_from = requestee_peer_id
3204                                    );
3205                                    task.metrics.discovery_addresses_dropped.inc(reason);
3206                                }
3207                            }
3208                            Err((error, addr)) => {
3209                                let reason = DiscoveredAddressDropReason::Invalid;
3210                                log!(
3211                                    &task.platform,
3212                                    Debug,
3213                                    "network",
3214                                    "discovered-address-dropped",
3215                                    chain = &task.network[chain_id].log_name,
3216                                    reason,
3217                                    peer_id,
3218                                    error,
3219                                    addr = hex::encode(&addr),
3220                                    obtained_from = requestee_peer_id
3221                                );
3222                                task.metrics.discovery_addresses_dropped.inc(reason);
3223                            }
3224                        }
3225                    }
3226
3227                    if !valid_addrs.is_empty() {
3228                        // Note that we must call this function before `insert_address`,
3229                        // as documented in `basic_peering_strategy`.
3230                        let insert_outcome =
3231                            task.peering_strategy
3232                                .insert_chain_peer(chain_id, peer_id.clone(), 30); // TODO: constant
3233
3234                        if let basic_peering_strategy::InsertChainPeerResult::Inserted {
3235                            peer_removed,
3236                        } = insert_outcome
3237                        {
3238                            any_new_peer = true;
3239                            if let Some(peer_removed) = peer_removed {
3240                                log!(
3241                                    &task.platform,
3242                                    Debug,
3243                                    "network",
3244                                    "peer-purged-from-address-book",
3245                                    chain = &task.network[chain_id].log_name,
3246                                    peer_id = peer_removed,
3247                                );
3248                            }
3249
3250                            log!(
3251                                &task.platform,
3252                                Debug,
3253                                "network",
3254                                "peer-discovered",
3255                                chain = &task.network[chain_id].log_name,
3256                                peer_id,
3257                                addrs = ?valid_addrs.iter().map(|a| a.to_string()).collect::<Vec<_>>(), // TODO: better formatting?
3258                                obtained_from = requestee_peer_id
3259                            );
3260                        }
3261                    }
3262
3263                    for addr in valid_addrs {
3264                        let _insert_result =
3265                            task.peering_strategy
3266                                .insert_address(&peer_id, addr.into_bytes(), 10); // TODO: constant
3267                        debug_assert!(!matches!(
3268                            _insert_result,
3269                            basic_peering_strategy::InsertAddressResult::UnknownPeer
3270                        ));
3271                    }
3272                }
3273
3274                if any_new_peer {
3275                    task.network[chain_id].next_discovery_period = Duration::from_secs(2);
3276                }
3277            }
3278            WakeUpReason::NetworkEvent(service::Event::RequestResult {
3279                peer_id,
3280                chain_id,
3281                response: service::RequestResult::KademliaFindNode(Err(error)),
3282                ..
3283            }) => {
3284                log!(
3285                    &task.platform,
3286                    Debug,
3287                    "network",
3288                    "discovery-find-node-error",
3289                    chain = &task.network[chain_id].log_name,
3290                    ?error,
3291                    find_node_target = peer_id,
3292                );
3293
3294                // No error is printed if the request fails due to a benign networking error such
3295                // as an unresponsive peer.
3296                match error {
3297                    service::KademliaFindNodeError::RequestFailed(err)
3298                        if !err.is_protocol_error() => {}
3299
3300                    service::KademliaFindNodeError::RequestFailed(
3301                        service::RequestError::Substream(
3302                            connection::established::RequestError::ProtocolNotAvailable,
3303                        ),
3304                    ) => {
3305                        // TODO: remove this warning in a long time
3306                        log!(
3307                            &task.platform,
3308                            Warn,
3309                            "network",
3310                            format!(
3311                                "Problem during discovery on {}: protocol not available. \
3312                                This might indicate that the version of Substrate used by \
3313                                the chain doesn't include \
3314                                <https://github.com/paritytech/substrate/pull/12545>.",
3315                                &task.network[chain_id].log_name
3316                            )
3317                        );
3318                    }
3319                    _ => {
3320                        log!(
3321                            &task.platform,
3322                            Debug,
3323                            "network",
3324                            format!(
3325                                "Problem during discovery on {}: {}",
3326                                &task.network[chain_id].log_name, error
3327                            )
3328                        );
3329                    }
3330                }
3331            }
3332            WakeUpReason::NetworkEvent(service::Event::RequestResult { .. }) => {
3333                // We never start any other kind of requests.
3334                unreachable!()
3335            }
3336            WakeUpReason::NetworkEvent(service::Event::GossipInDesired {
3337                peer_id,
3338                chain_id,
3339                kind: service::GossipKind::ConsensusTransactions,
3340            }) => {
3341                // The networking state machine guarantees that `GossipInDesired`
3342                // can't happen if we are already opening an out slot, which we do
3343                // immediately.
3344                // TODO: add debug_assert! ^
3345
3346                // Statement links also appear among the opened undesired links and must
3347                // not use up the inbound block announces slots.
3348                if task
3349                    .network
3350                    .opened_gossip_undesired_by_chain(chain_id)
3351                    .filter(|(_, kind)| *kind == service::GossipKind::ConsensusTransactions)
3352                    .count()
3353                    < 4
3354                {
3355                    log!(
3356                        &task.platform,
3357                        Debug,
3358                        "network",
3359                        "gossip-in-request",
3360                        chain = &task.network[chain_id].log_name,
3361                        peer_id,
3362                        outcome = "accepted"
3363                    );
3364                    task.network
3365                        .gossip_open(
3366                            chain_id,
3367                            &peer_id,
3368                            service::GossipKind::ConsensusTransactions,
3369                        )
3370                        .unwrap();
3371                } else {
3372                    log!(
3373                        &task.platform,
3374                        Debug,
3375                        "network",
3376                        "gossip-in-request",
3377                        chain = &task.network[chain_id].log_name,
3378                        peer_id,
3379                        outcome = "rejected",
3380                    );
3381                    task.network
3382                        .gossip_close(
3383                            chain_id,
3384                            &peer_id,
3385                            service::GossipKind::ConsensusTransactions,
3386                        )
3387                        .unwrap();
3388                }
3389            }
3390            WakeUpReason::NetworkEvent(service::Event::GossipInDesiredCancel { .. }) => {
3391                // Can't happen as we already instantaneously accept or reject gossip in requests.
3392                unreachable!()
3393            }
3394            WakeUpReason::NetworkEvent(
3395                service::Event::GossipConnected {
3396                    kind: service::GossipKind::Statement,
3397                    ..
3398                }
3399                | service::Event::GossipOpenFailed {
3400                    kind: service::GossipKind::Statement,
3401                    ..
3402                }
3403                | service::Event::GossipDisconnected {
3404                    kind: service::GossipKind::Statement,
3405                    ..
3406                }
3407                | service::Event::GossipInDesired {
3408                    kind: service::GossipKind::Statement,
3409                    ..
3410                },
3411            ) => {
3412                // Can't happen as the statement kind reports through the `StatementProtocol*`
3413                // events, and inbound statement substreams are accepted or refused without
3414                // asking.
3415                unreachable!()
3416            }
3417            WakeUpReason::NetworkEvent(service::Event::IdentifyRequestIn {
3418                peer_id,
3419                substream_id,
3420            }) => {
3421                log!(
3422                    &task.platform,
3423                    Debug,
3424                    "network",
3425                    "identify-request-received",
3426                    peer_id,
3427                );
3428                task.network
3429                    .respond_identify(substream_id, &task.identify_agent_version);
3430            }
3431            WakeUpReason::NetworkEvent(service::Event::BlocksRequestIn { .. }) => unreachable!(),
3432            WakeUpReason::NetworkEvent(service::Event::RequestInCancel { .. }) => {
3433                // All incoming requests are immediately answered.
3434                unreachable!()
3435            }
3436            WakeUpReason::NetworkEvent(service::Event::GrandpaNeighborPacket {
3437                chain_id,
3438                peer_id,
3439                state,
3440            }) => {
3441                log!(
3442                    &task.platform,
3443                    Debug,
3444                    "network",
3445                    "grandpa-neighbor-packet-received",
3446                    chain = &task.network[chain_id].log_name,
3447                    peer_id,
3448                    round_number = state.round_number,
3449                    set_id = state.set_id,
3450                    commit_finalized_height = state.commit_finalized_height,
3451                );
3452
3453                task.open_gossip_links
3454                    .get_mut(&(chain_id, peer_id.clone()))
3455                    .unwrap()
3456                    .finalized_block_height = Some(state.commit_finalized_height);
3457
3458                debug_assert!(task.events_pending_send.is_empty());
3459                task.events_pending_send.push_back((
3460                    chain_id,
3461                    Event::GrandpaNeighborPacket {
3462                        peer_id,
3463                        finalized_block_height: state.commit_finalized_height,
3464                    },
3465                ));
3466            }
3467            WakeUpReason::NetworkEvent(service::Event::GrandpaCommitMessage {
3468                chain_id,
3469                peer_id,
3470                message,
3471            }) => {
3472                log!(
3473                    &task.platform,
3474                    Debug,
3475                    "network",
3476                    "grandpa-commit-message-received",
3477                    chain = &task.network[chain_id].log_name,
3478                    peer_id,
3479                    target_block_hash = HashDisplay(message.decode().target_hash),
3480                );
3481
3482                debug_assert!(task.events_pending_send.is_empty());
3483                task.events_pending_send
3484                    .push_back((chain_id, Event::GrandpaCommitMessage { peer_id, message }));
3485            }
3486            WakeUpReason::NetworkEvent(service::Event::StatementsNotification {
3487                chain_id,
3488                peer_id,
3489                statements,
3490            }) => {
3491                debug_assert!(task.statement_event_pending_send.is_none());
3492
3493                if statements.is_empty() {
3494                    continue;
3495                }
3496
3497                task.statement_event_pending_send = Some((
3498                    chain_id,
3499                    StatementEvent::StatementsNotification {
3500                        peer_id,
3501                        statements,
3502                    },
3503                ));
3504            }
3505            WakeUpReason::NetworkEvent(service::Event::StatementProtocolConnected {
3506                peer_id,
3507                chain_id,
3508                version,
3509            }) => {
3510                log!(
3511                    &task.platform,
3512                    Trace,
3513                    "network",
3514                    "statement-protocol-open-success",
3515                    chain = &task.network[chain_id].log_name,
3516                    peer_id,
3517                    ?version,
3518                );
3519
3520                if matches!(version, codec::StatementProtocolVersion::V2) {
3521                    task.v2_statement_peers
3522                        .entry(chain_id)
3523                        .or_insert_with(|| {
3524                            HashSet::with_capacity_and_hasher(16, Default::default())
3525                        })
3526                        .insert(peer_id.clone());
3527                    if let Some(filter) = task.current_affinity_filter.get(&chain_id) {
3528                        if let Err(
3529                            SendTopicAffinityError::NoConnection
3530                            | SendTopicAffinityError::ProtocolV1,
3531                        ) = task.network.send_topic_affinity(&peer_id, chain_id, filter)
3532                        {
3533                            task.v2_statement_peers
3534                                .get_mut(&chain_id)
3535                                .unwrap()
3536                                .remove(&peer_id);
3537                        }
3538                    }
3539                }
3540            }
3541            WakeUpReason::NetworkEvent(service::Event::StatementProtocolOpenFailed {
3542                peer_id,
3543                chain_id,
3544                error,
3545            }) => {
3546                log!(
3547                    &task.platform,
3548                    Debug,
3549                    "network",
3550                    "statement-protocol-open-error",
3551                    chain = &task.network[chain_id].log_name,
3552                    peer_id,
3553                    ?error,
3554                );
3555
3556                // A peer still desired under the statement kind would be opened again at
3557                // once. Whoever marks a peer as desired under that kind decides whether to
3558                // retry.
3559                task.network.gossip_remove_desired(
3560                    chain_id,
3561                    &peer_id,
3562                    service::GossipKind::Statement,
3563                );
3564            }
3565            WakeUpReason::NetworkEvent(service::Event::StatementProtocolDisconnected {
3566                peer_id,
3567                chain_id,
3568            }) => {
3569                log!(
3570                    &task.platform,
3571                    Debug,
3572                    "network",
3573                    "statement-protocol-closed",
3574                    chain = &task.network[chain_id].log_name,
3575                    peer_id,
3576                );
3577
3578                // A peer still desired under the statement kind would be opened again at
3579                // once, as after an open failure.
3580                task.network.gossip_remove_desired(
3581                    chain_id,
3582                    &peer_id,
3583                    service::GossipKind::Statement,
3584                );
3585
3586                if let Some(peers) = task.v2_statement_peers.get_mut(&chain_id) {
3587                    peers.remove(&peer_id);
3588                }
3589            }
3590            // TODO: we don't filter outbound statements yet
3591            WakeUpReason::NetworkEvent(service::Event::StatementTopicAffinityReceived {
3592                ..
3593            }) => {}
3594            WakeUpReason::NetworkEvent(service::Event::ProtocolError { peer_id, error }) => {
3595                // TODO: handle properly?
3596                log!(
3597                    &task.platform,
3598                    Warn,
3599                    "network",
3600                    "protocol-error",
3601                    peer_id,
3602                    ?error
3603                );
3604
3605                // TODO: disconnect peer
3606            }
3607            WakeUpReason::CanAssignSlot(peer_id, chain_id) => {
3608                task.peering_strategy.assign_slot(&chain_id, &peer_id);
3609
3610                log!(
3611                    &task.platform,
3612                    Debug,
3613                    "network",
3614                    "slot-assigned",
3615                    chain = &task.network[chain_id].log_name,
3616                    peer_id
3617                );
3618
3619                task.network.gossip_insert_desired(
3620                    chain_id,
3621                    peer_id,
3622                    service::GossipKind::ConsensusTransactions,
3623                );
3624            }
3625            WakeUpReason::CanAssignBitswapSlot(peer_id) => {
3626                task.bitswap_peering_strategy.assign_slot(&peer_id).unwrap();
3627
3628                log!(
3629                    &task.platform,
3630                    Debug,
3631                    "network",
3632                    "bitswap-slot-assigned",
3633                    peer_id
3634                );
3635
3636                task.network.bitswap_insert_desired(peer_id);
3637            }
3638            WakeUpReason::NextRecentConnectionRestore => {
3639                task.num_recent_connection_opening =
3640                    task.num_recent_connection_opening.saturating_sub(1);
3641            }
3642            WakeUpReason::CanStartConnect(expected_peer_id) => {
3643                let Some(multiaddr) = task
3644                    .peering_strategy
3645                    .pick_address_and_add_connection(&expected_peer_id)
3646                else {
3647                    // There is no address for that peer in the address book.
3648                    task.network.gossip_remove_desired_all(
3649                        &expected_peer_id,
3650                        service::GossipKind::ConsensusTransactions,
3651                    );
3652                    task.network.gossip_remove_desired_all(
3653                        &expected_peer_id,
3654                        service::GossipKind::Statement,
3655                    );
3656                    let ban_duration = Duration::from_secs(10);
3657                    for (&chain_id, what_happened) in task.peering_strategy.unassign_slots_and_ban(
3658                        &expected_peer_id,
3659                        task.platform.now() + ban_duration,
3660                    ) {
3661                        if matches!(
3662                            what_happened,
3663                            basic_peering_strategy::UnassignSlotsAndBan::Banned { had_slot: true }
3664                        ) {
3665                            log!(
3666                                &task.platform,
3667                                Debug,
3668                                "network",
3669                                "slot-unassigned",
3670                                chain = &task.network[chain_id].log_name,
3671                                peer_id = expected_peer_id,
3672                                ?ban_duration,
3673                                reason = "no-address"
3674                            );
3675                        }
3676                    }
3677                    continue;
3678                };
3679
3680                let multiaddr = match multiaddr::Multiaddr::from_bytes(multiaddr.to_owned()) {
3681                    Ok(a) => a,
3682                    Err((multiaddr::FromBytesError, addr)) => {
3683                        // Address is in an invalid format.
3684                        let _was_in = task
3685                            .peering_strategy
3686                            .decrease_address_connections_and_remove_if_zero(
3687                                &expected_peer_id,
3688                                &addr,
3689                            );
3690                        debug_assert!(_was_in.is_ok());
3691                        continue;
3692                    }
3693                };
3694
3695                let address = address_parse::multiaddr_to_address(&multiaddr)
3696                    .ok()
3697                    .filter(|addr| {
3698                        task.platform.supports_connection_type(match &addr {
3699                            address_parse::AddressOrMultiStreamAddress::Address(addr) => {
3700                                From::from(addr)
3701                            }
3702                            address_parse::AddressOrMultiStreamAddress::MultiStreamAddress(
3703                                addr,
3704                            ) => From::from(addr),
3705                        })
3706                    });
3707
3708                let Some(address) = address else {
3709                    // Address is in an invalid format or isn't supported by the platform.
3710                    let _was_in = task
3711                        .peering_strategy
3712                        .decrease_address_connections_and_remove_if_zero(
3713                            &expected_peer_id,
3714                            multiaddr.as_ref(),
3715                        );
3716                    debug_assert!(_was_in.is_ok());
3717                    continue;
3718                };
3719
3720                // Each connection has its own individual Noise key.
3721                let noise_key = {
3722                    let mut noise_static_key = zeroize::Zeroizing::new([0u8; 32]);
3723                    task.platform.fill_random_bytes(&mut *noise_static_key);
3724                    let mut libp2p_key = zeroize::Zeroizing::new([0u8; 32]);
3725                    task.platform.fill_random_bytes(&mut *libp2p_key);
3726                    connection::NoiseKey::new(&libp2p_key, &noise_static_key)
3727                };
3728
3729                log!(
3730                    &task.platform,
3731                    Debug,
3732                    "network",
3733                    "connection-started",
3734                    expected_peer_id,
3735                    remote_addr = multiaddr,
3736                    local_peer_id =
3737                        peer_id::PublicKey::Ed25519(*noise_key.libp2p_public_ed25519_key())
3738                            .into_peer_id(),
3739                );
3740
3741                task.num_recent_connection_opening += 1;
3742                task.metrics.connections_dialed.inc();
3743
3744                let (coordinator_to_connection_tx, coordinator_to_connection_rx) =
3745                    async_channel::bounded(8);
3746                let task_name = format!("connection-{}", multiaddr);
3747
3748                match address {
3749                    address_parse::AddressOrMultiStreamAddress::Address(address) => {
3750                        // As documented in the `PlatformRef` trait, `connect_stream` must
3751                        // return as soon as possible.
3752                        let connection = task.platform.connect_stream(address).await;
3753
3754                        let (connection_id, connection_task) =
3755                            task.network.add_single_stream_connection(
3756                                task.platform.now(),
3757                                service::SingleStreamHandshakeKind::MultistreamSelectNoiseYamux {
3758                                    is_initiator: true,
3759                                    noise_key: &noise_key,
3760                                },
3761                                multiaddr.clone().into_bytes(),
3762                                Some(expected_peer_id.clone()),
3763                                coordinator_to_connection_tx,
3764                            );
3765
3766                        task.platform.spawn_task(
3767                            task_name.into(),
3768                            tasks::single_stream_connection_task::<TPlat>(
3769                                connection,
3770                                multiaddr.to_string(),
3771                                task.platform.clone(),
3772                                connection_id,
3773                                connection_task,
3774                                coordinator_to_connection_rx,
3775                                task.tasks_messages_tx.clone(),
3776                            ),
3777                        );
3778                    }
3779                    address_parse::AddressOrMultiStreamAddress::MultiStreamAddress(address) => {
3780                        let remote_certificate_sha256 = match &address {
3781                            platform::MultiStreamAddress::WebRtc {
3782                                remote_certificate_sha256,
3783                                ..
3784                            }
3785                            | platform::MultiStreamAddress::WebRtcDns {
3786                                remote_certificate_sha256,
3787                                ..
3788                            } => *remote_certificate_sha256,
3789                        };
3790
3791                        // We need to know the local TLS certificate in order to insert the
3792                        // connection, and as such we need to call `connect_multistream` here.
3793                        // As documented in the `PlatformRef` trait, `connect_multistream` must
3794                        // return as soon as possible.
3795                        let connection = task.platform.connect_multistream(address).await;
3796
3797                        // Convert the SHA256 hashes into multihashes.
3798                        let local_tls_certificate_multihash = [18u8, 32]
3799                            .into_iter()
3800                            .chain(connection.local_tls_certificate_sha256.into_iter())
3801                            .collect();
3802                        let remote_tls_certificate_multihash = [18u8, 32]
3803                            .into_iter()
3804                            .chain(remote_certificate_sha256.iter().copied())
3805                            .collect();
3806
3807                        let (connection_id, connection_task) =
3808                            task.network.add_multi_stream_connection(
3809                                task.platform.now(),
3810                                service::MultiStreamHandshakeKind::WebRtc {
3811                                    is_initiator: true,
3812                                    local_tls_certificate_multihash,
3813                                    remote_tls_certificate_multihash,
3814                                    noise_key: &noise_key,
3815                                },
3816                                multiaddr.clone().into_bytes(),
3817                                Some(expected_peer_id.clone()),
3818                                coordinator_to_connection_tx,
3819                            );
3820
3821                        task.platform.spawn_task(
3822                            task_name.into(),
3823                            tasks::webrtc_multi_stream_connection_task::<TPlat>(
3824                                connection.connection,
3825                                multiaddr.to_string(),
3826                                task.platform.clone(),
3827                                connection_id,
3828                                connection_task,
3829                                coordinator_to_connection_rx,
3830                                task.tasks_messages_tx.clone(),
3831                            ),
3832                        );
3833                    }
3834                }
3835            }
3836            WakeUpReason::CanOpenGossip(peer_id, chain_id, kind) => {
3837                task.network.gossip_open(chain_id, &peer_id, kind).unwrap();
3838
3839                log!(
3840                    &task.platform,
3841                    Debug,
3842                    "network",
3843                    "gossip-open-start",
3844                    chain = &task.network[chain_id].log_name,
3845                    peer_id,
3846                    ?kind,
3847                );
3848            }
3849            WakeUpReason::CanOpenBitswap(peer_id) => {
3850                task.network.bitswap_open(&peer_id).unwrap();
3851
3852                log!(
3853                    &task.platform,
3854                    Debug,
3855                    "network",
3856                    "bitswap-open-start",
3857                    peer_id
3858                );
3859            }
3860            WakeUpReason::MessageToConnection {
3861                connection_id,
3862                message,
3863            } => {
3864                // Note that it is critical for the sending to not take too long here, in order to
3865                // not block the process of the network service.
3866                // In particular, if sending the message to the connection is blocked due to
3867                // sending a message on the connection-to-coordinator channel, this will result
3868                // in a deadlock.
3869                // For this reason, the connection task is always ready to immediately accept a
3870                // message on the coordinator-to-connection channel.
3871                let _send_result = task.network[connection_id].send(message).await;
3872                debug_assert!(_send_result.is_ok());
3873            }
3874        }
3875    }
3876}
3877
3878/// Starts find-node requests against `candidates` until `max` have started, each with a fresh
3879/// random target key, and returns the `(request_target, requested_peer_id)` of each.
3880///
3881/// A `kademlia_capable_peers` candidate may have no usable connection (the flag outlives the
3882/// connection it was learned on); such a peer fails with `NoConnection` and is skipped without
3883/// counting towards `max`.
3884fn dispatch_find_node_requests<TChain, TConn, TNow>(
3885    network: &mut service::ChainNetwork<TChain, TConn, TNow>,
3886    randomness: &mut impl rand_chacha::rand_core::RngCore,
3887    chain_id: service::ChainId,
3888    candidates: &[PeerId],
3889    max: usize,
3890) -> Vec<(PeerId, PeerId)>
3891where
3892    TNow: Clone
3893        + core::ops::Add<Duration, Output = TNow>
3894        + core::ops::Sub<TNow, Output = Duration>
3895        + Ord,
3896{
3897    let mut started = Vec::with_capacity(max);
3898
3899    for target in candidates {
3900        if started.len() >= max {
3901            break;
3902        }
3903
3904        let random_peer_id = {
3905            let mut pub_key = [0; 32];
3906            randomness.fill_bytes(&mut pub_key);
3907            PeerId::from_public_key(&peer_id::PublicKey::Ed25519(pub_key))
3908        };
3909
3910        match network.start_kademlia_find_node_request(
3911            target,
3912            chain_id,
3913            &random_peer_id,
3914            Duration::from_secs(20),
3915        ) {
3916            Ok(_) => started.push((target.clone(), random_peer_id)),
3917            Err(service::StartRequestError::NoConnection) => {}
3918        }
3919    }
3920
3921    started
3922}
3923
3924/// Pops a trailing `/p2p/<peer_id>` from `addr` if it matches `expected_peer`. Returns `false`
3925/// (caller must discard the address) on mismatch.
3926fn pop_p2p_if_matches(
3927    addr: &mut smoldot::libp2p::multiaddr::Multiaddr,
3928    expected_peer: &smoldot::libp2p::peer_id::PeerId,
3929) -> bool {
3930    use smoldot::libp2p::multiaddr::Protocol;
3931    match addr.iter().last() {
3932        Some(Protocol::P2p(mh)) => {
3933            if mh.into_bytes() == expected_peer.as_bytes() {
3934                addr.pop();
3935                true
3936            } else {
3937                false
3938            }
3939        }
3940        _ => true,
3941    }
3942}
3943
3944#[cfg(test)]
3945mod tests {
3946    use super::{Role, dispatch_find_node_requests, pop_p2p_if_matches, service};
3947    use core::time::Duration;
3948    use rand_chacha::rand_core::SeedableRng as _;
3949    use smoldot::libp2p::{multiaddr::Multiaddr, peer_id::PeerId};
3950
3951    // Two distinct, valid PeerIds. The first is reused from existing smoldot tests in
3952    // `lib/src/libp2p/multiaddr.rs:629`; the second is the bootnode peer-id observed in the
3953    // test environment that motivated this change.
3954    const PEER_A: &str = "12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN";
3955    const PEER_B: &str = "12D3KooWQk1yQtG1YugyKjiQf6KNk8VjGGAT5xy1FWcnRKN4yXYJ";
3956
3957    fn peer(s: &str) -> PeerId {
3958        PeerId::from_bytes(bs58::decode(s).into_vec().unwrap()).unwrap()
3959    }
3960
3961    #[test]
3962    fn no_suffix_passes_through_unchanged() {
3963        let mut addr: Multiaddr = "/ip4/127.0.0.1/tcp/30333/ws".parse().unwrap();
3964        let before = addr.clone();
3965        assert!(pop_p2p_if_matches(&mut addr, &peer(PEER_A)));
3966        assert_eq!(addr, before);
3967    }
3968
3969    #[test]
3970    fn matching_suffix_is_stripped() {
3971        let mut addr: Multiaddr = format!("/ip4/127.0.0.1/tcp/30333/ws/p2p/{PEER_A}")
3972            .parse()
3973            .unwrap();
3974        assert!(pop_p2p_if_matches(&mut addr, &peer(PEER_A)));
3975        let expected: Multiaddr = "/ip4/127.0.0.1/tcp/30333/ws".parse().unwrap();
3976        assert_eq!(addr, expected);
3977    }
3978
3979    #[test]
3980    fn mismatched_suffix_rejects_and_keeps_addr() {
3981        let original: Multiaddr = format!("/ip4/127.0.0.1/tcp/30333/ws/p2p/{PEER_A}")
3982            .parse()
3983            .unwrap();
3984        let mut addr = original.clone();
3985        assert!(!pop_p2p_if_matches(&mut addr, &peer(PEER_B)));
3986        assert_eq!(addr, original);
3987    }
3988
3989    fn empty_network() -> (service::ChainNetwork<(), (), Duration>, service::ChainId) {
3990        let mut network = service::ChainNetwork::new(service::Config {
3991            connections_capacity: 8,
3992            chains_capacity: 1,
3993            randomness_seed: [0; 32],
3994            handshake_timeout: Duration::from_secs(10),
3995        });
3996        let chain_id = network
3997            .add_chain(service::ChainConfig {
3998                user_data: (),
3999                genesis_hash: [0; 32],
4000                fork_id: None,
4001                block_number_bytes: 4,
4002                grandpa_protocol_config: None,
4003                allow_inbound_block_requests: false,
4004                best_hash: [0; 32],
4005                best_number: 0,
4006                role: Role::Light,
4007                enable_statement_protocol: false,
4008            })
4009            .unwrap();
4010        (network, chain_id)
4011    }
4012
4013    // With no connections every candidate returns `NoConnection`, so all are skipped and no
4014    // request is started. The dispatch loop must not treat that as unreachable.
4015    #[test]
4016    fn dispatch_skips_unreachable_candidates() {
4017        let (mut network, chain_id) = empty_network();
4018        let mut randomness = rand_chacha::ChaCha20Rng::from_seed([7; 32]);
4019
4020        let candidates = [peer(PEER_A), peer(PEER_B)];
4021        let started =
4022            dispatch_find_node_requests(&mut network, &mut randomness, chain_id, &candidates, 3);
4023
4024        assert!(started.is_empty());
4025    }
4026}