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