Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/network/basic_peering_strategy.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2023  Pierre Krieger
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
//! Basic address book and slots assignments algorithm.
19
//!
20
//! The [`BasicPeeringStrategy`] contains a collection of network identities, identified by
21
//! a [`PeerId`]. Each network identity is associated to one or more chains, identified by a
22
//! `TChainId`.
23
//!
24
//! Each network-identity-chain association can be in one of these three states:
25
//!
26
//! - Normal.
27
//! - Banned until a certain instant represented by `TInstant`.
28
//! - Has a slot.
29
//!
30
//! "Normal" and "banned" network-identity-chain associations represent the potential peers to
31
//! connect to, while "slot" represent pending or established gossip slots.
32
//!
33
//! Use [`BasicPeeringStrategy::pick_assignable_peer`] in order to assign a slot to a
34
//! randomly-chosen network-identity that doesn't currently have one.
35
//!
36
//! If a gossip slot fails to be established with a certain peer, or if the peer misbehaves,
37
//! use [`BasicPeeringStrategy::unassign_slot_and_ban`] to ban the peer, preventing it from
38
//! obtaining a slot for a certain amount of time.
39
//!
40
//! Each network identity that is associated with at least one chain is associated with zero or
41
//! more addresses. It is not possible to insert addresses to peers that aren't associated to at
42
//! least one chain. The number of active connections of each address is also tracked.
43
//!
44
//! There exists a limit to the number of peers per chain and the number of addresses per peer,
45
//! guaranteeing that the data structure only uses a bounded amount of memory. If these limits
46
//! are reached, peers and addresses are removed randomly. Peers that have a slot and at least one
47
//! connected address are never removed.
48
//!
49
50
use crate::util;
51
use alloc::{
52
    borrow::ToOwned as _,
53
    collections::{btree_map, BTreeMap, BTreeSet},
54
    vec::Vec,
55
};
56
use core::{hash::Hash, iter, ops};
57
use rand::seq::IteratorRandom as _;
58
use rand_chacha::{
59
    rand_core::{RngCore as _, SeedableRng as _},
60
    ChaCha20Rng,
61
};
62
63
pub use crate::libp2p::PeerId;
64
65
#[derive(Debug)]
66
pub struct BasicPeeringStrategy<TChainId, TInstant> {
67
    /// Contains all the `PeerId`s used throughout the collection.
68
    peer_ids: slab::Slab<PeerId>,
69
70
    /// Contains all the keys of [`BasicPeeringStrategy::peer_ids`] indexed differently.
71
    peer_ids_indices: hashbrown::HashMap<PeerId, usize, util::SipHasherBuild>,
72
73
    /// List of all known addresses, indexed by `peer_id_index`, with the number of connections to
74
    /// each address. The addresses are not intended to be in a particular order.
75
    addresses: BTreeMap<(usize, Vec<u8>), u32>,
76
77
    /// List of all chains throughout the collection.
78
    ///
79
    /// > **Note**: In principle this field is completely unnecessary. In practice, however, we
80
    /// >           can't use `BTreeMap::range` with `TChainId`s because we don't know the minimum
81
    /// >           and maximum values of a `TChainId`. In order to bypass this problem,
82
    /// >           `TChainId`s are instead refered to as a `usize`.
83
    chains: slab::Slab<TChainId>,
84
85
    /// Contains all the keys of [`BasicPeeringStrategy::chains`] indexed differently.
86
    /// While a dumber hasher is in principle enough, we use a `SipHasherBuild` "just in case"
87
    /// as we don't know the properties of `TChainId`.
88
    chains_indices: hashbrown::HashMap<TChainId, usize, util::SipHasherBuild>,
89
90
    /// Collection of
91
    /// Keys are `(peer_id_index, chain_id_index)`.
92
    peers_chains: BTreeMap<(usize, usize), PeerChainState<TInstant>>,
93
94
    /// Entries are `(chain_id_index, state, peer_id_index)`.
95
    peers_chains_by_state: BTreeSet<(usize, PeerChainState<TInstant>, usize)>,
96
97
    /// Random number generator used to select peers to assign slots to and remove addresses/peers.
98
    randomness: ChaCha20Rng,
99
}
100
101
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
102
enum PeerChainState<TInstant> {
103
    Assignable,
104
    Banned { expires: TInstant },
105
    Slot,
106
}
107
108
/// Configuration passed to [`BasicPeeringStrategy::new`].
109
pub struct Config {
110
    /// Seed used for the randomness for choosing peers and addresses to connect to or remove.
111
    pub randomness_seed: [u8; 32],
112
113
    /// Number of peers, all chains together, to initially reserve memory for.
114
    pub peers_capacity: usize,
115
116
    /// Number of chains to initially reserve memory for.
117
    pub chains_capacity: usize,
118
}
119
120
impl<TChainId, TInstant> BasicPeeringStrategy<TChainId, TInstant>
121
where
122
    TChainId: PartialOrd + Ord + Eq + Hash + Clone,
123
    TInstant: PartialOrd + Ord + Eq + Clone,
124
{
125
    /// Creates a new empty [`BasicPeeringStrategy`].
126
    ///
127
    /// Must be passed a seed for randomness used
128
    /// in [`BasicPeeringStrategy::pick_assignable_peer`].
129
25
    pub fn new(config: Config) -> Self {
130
25
        let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
131
25
132
25
        BasicPeeringStrategy {
133
25
            peer_ids: slab::Slab::with_capacity(config.peers_capacity),
134
25
            peer_ids_indices: hashbrown::HashMap::with_capacity_and_hasher(
135
25
                config.peers_capacity,
136
25
                util::SipHasherBuild::new({
137
25
                    let mut seed = [0; 16];
138
25
                    randomness.fill_bytes(&mut seed);
139
25
                    seed
140
25
                }),
141
25
            ),
142
25
            addresses: BTreeMap::new(),
143
25
            chains: slab::Slab::with_capacity(config.chains_capacity),
144
25
            chains_indices: hashbrown::HashMap::with_capacity_and_hasher(
145
25
                config.chains_capacity,
146
25
                util::SipHasherBuild::new({
147
25
                    let mut seed = [0; 16];
148
25
                    randomness.fill_bytes(&mut seed);
149
25
                    seed
150
25
                }),
151
25
            ),
152
25
            peers_chains: BTreeMap::new(),
153
25
            peers_chains_by_state: BTreeSet::new(),
154
25
            randomness,
155
25
        }
156
25
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE3newB6_
Line
Count
Source
129
4
    pub fn new(config: Config) -> Self {
130
4
        let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
131
4
132
4
        BasicPeeringStrategy {
133
4
            peer_ids: slab::Slab::with_capacity(config.peers_capacity),
134
4
            peer_ids_indices: hashbrown::HashMap::with_capacity_and_hasher(
135
4
                config.peers_capacity,
136
4
                util::SipHasherBuild::new({
137
4
                    let mut seed = [0; 16];
138
4
                    randomness.fill_bytes(&mut seed);
139
4
                    seed
140
4
                }),
141
4
            ),
142
4
            addresses: BTreeMap::new(),
143
4
            chains: slab::Slab::with_capacity(config.chains_capacity),
144
4
            chains_indices: hashbrown::HashMap::with_capacity_and_hasher(
145
4
                config.chains_capacity,
146
4
                util::SipHasherBuild::new({
147
4
                    let mut seed = [0; 16];
148
4
                    randomness.fill_bytes(&mut seed);
149
4
                    seed
150
4
                }),
151
4
            ),
152
4
            peers_chains: BTreeMap::new(),
153
4
            peers_chains_by_state: BTreeSet::new(),
154
4
            randomness,
155
4
        }
156
4
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE3newCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE3newB6_
_RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE3newCsiLzmwikkc22_14json_rpc_basic
Line
Count
Source
129
2
    pub fn new(config: Config) -> Self {
130
2
        let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
131
2
132
2
        BasicPeeringStrategy {
133
2
            peer_ids: slab::Slab::with_capacity(config.peers_capacity),
134
2
            peer_ids_indices: hashbrown::HashMap::with_capacity_and_hasher(
135
2
                config.peers_capacity,
136
2
                util::SipHasherBuild::new({
137
2
                    let mut seed = [0; 16];
138
2
                    randomness.fill_bytes(&mut seed);
139
2
                    seed
140
2
                }),
141
2
            ),
142
2
            addresses: BTreeMap::new(),
143
2
            chains: slab::Slab::with_capacity(config.chains_capacity),
144
2
            chains_indices: hashbrown::HashMap::with_capacity_and_hasher(
145
2
                config.chains_capacity,
146
2
                util::SipHasherBuild::new({
147
2
                    let mut seed = [0; 16];
148
2
                    randomness.fill_bytes(&mut seed);
149
2
                    seed
150
2
                }),
151
2
            ),
152
2
            peers_chains: BTreeMap::new(),
153
2
            peers_chains_by_state: BTreeSet::new(),
154
2
            randomness,
155
2
        }
156
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE3newCscDgN54JpMGG_6author
_RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE3newCsibGXYHQB8Ea_25json_rpc_general_requests
Line
Count
Source
129
19
    pub fn new(config: Config) -> Self {
130
19
        let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
131
19
132
19
        BasicPeeringStrategy {
133
19
            peer_ids: slab::Slab::with_capacity(config.peers_capacity),
134
19
            peer_ids_indices: hashbrown::HashMap::with_capacity_and_hasher(
135
19
                config.peers_capacity,
136
19
                util::SipHasherBuild::new({
137
19
                    let mut seed = [0; 16];
138
19
                    randomness.fill_bytes(&mut seed);
139
19
                    seed
140
19
                }),
141
19
            ),
142
19
            addresses: BTreeMap::new(),
143
19
            chains: slab::Slab::with_capacity(config.chains_capacity),
144
19
            chains_indices: hashbrown::HashMap::with_capacity_and_hasher(
145
19
                config.chains_capacity,
146
19
                util::SipHasherBuild::new({
147
19
                    let mut seed = [0; 16];
148
19
                    randomness.fill_bytes(&mut seed);
149
19
                    seed
150
19
                }),
151
19
            ),
152
19
            peers_chains: BTreeMap::new(),
153
19
            peers_chains_by_state: BTreeSet::new(),
154
19
            randomness,
155
19
        }
156
19
    }
157
158
    /// Removes all the chain assignments for the given chain.
159
    ///
160
    /// If a peer isn't assigned to any chain anymore and doesn't have any connected address,
161
    /// all of its addresses are also removed from the collection.
162
0
    pub fn remove_chain_peers(&mut self, chain: &TChainId) {
163
0
        let Some(chain_index) = self.chains_indices.remove(chain) else {
164
            // Chain didn't exist.
165
0
            return;
166
        };
167
0
        self.chains.remove(chain_index);
168
0
169
0
        let chain_peers = {
170
0
            let mut in_chain_and_after_chain = self.peers_chains_by_state.split_off(&(
171
0
                chain_index,
172
0
                PeerChainState::Assignable,
173
0
                usize::MIN,
174
0
            ));
175
0
            let mut after_chain = in_chain_and_after_chain.split_off(&(
176
0
                chain_index + 1,
177
0
                PeerChainState::Assignable,
178
0
                usize::MIN,
179
0
            ));
180
0
            self.peers_chains_by_state.append(&mut after_chain);
181
0
            in_chain_and_after_chain
182
        };
183
184
0
        for (_, _, peer_id_index) in chain_peers {
185
0
            let _was_in = self.peers_chains.remove(&(peer_id_index, chain_index));
186
0
            debug_assert!(_was_in.is_some());
187
0
            self.try_clean_up_peer_id(peer_id_index);
188
        }
189
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE18remove_chain_peersB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE18remove_chain_peersCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE18remove_chain_peersB6_
190
191
    /// Inserts a chain-peer combination to the collection, indicating that the given peer belongs
192
    /// to the given chain.
193
    ///
194
    /// Has no effect if the peer is already assigned to the given chain, in which case
195
    /// [`InsertChainPeerResult::Duplicate`] is returned.
196
    ///
197
    /// A maximum number of peers per chain must be provided. If the peer is inserted and the
198
    /// limit is exceeded, a peer (other than the one that has just been inserted) that belongs
199
    /// to the given chain is randomly chosen and removed. Peers that have slots assigned to them
200
    /// are never removed.
201
2
    pub fn insert_chain_peer(
202
2
        &mut self,
203
2
        chain: TChainId,
204
2
        peer_id: PeerId,
205
2
        max_peers_per_chain: usize,
206
2
    ) -> InsertChainPeerResult {
207
2
        let peer_id_index = self.get_or_insert_peer_index(&peer_id);
208
2
        let chain_index = self.get_or_insert_chain_index(&chain);
209
210
2
        if let btree_map::Entry::Vacant(entry) =
211
2
            self.peers_chains.entry((peer_id_index, chain_index))
212
        {
213
2
            let peer_to_remove = if self
214
2
                .peers_chains_by_state
215
2
                .range(
216
2
                    (chain_index, PeerChainState::Assignable, usize::MIN)
217
2
                        ..=(chain_index, PeerChainState::Slot, usize::MAX),
218
2
                )
219
2
                .count()
220
2
                >= max_peers_per_chain
221
            {
222
0
                self.peers_chains_by_state
223
0
                    .range(
224
0
                        (chain_index, PeerChainState::Assignable, usize::MIN)
225
0
                            ..(chain_index, PeerChainState::Slot, usize::MIN),
226
0
                    )
227
0
                    .choose(&mut self.randomness)
228
0
                    .map(|(_, _, peer_index)| *peer_index)
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE17insert_chain_peer0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE17insert_chain_peer0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE17insert_chain_peer0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peer0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peer0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peer0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peer0CsibGXYHQB8Ea_25json_rpc_general_requests
229
            } else {
230
2
                None
231
            };
232
233
2
            let _was_inserted = self.peers_chains_by_state.insert((
234
2
                chain_index,
235
2
                PeerChainState::Assignable,
236
2
                peer_id_index,
237
2
            ));
238
2
            debug_assert!(_was_inserted);
239
240
2
            entry.insert(PeerChainState::Assignable);
241
242
2
            let peer_removed = if let Some(
peer_to_remove0
) = peer_to_remove {
243
0
                let peer_id_to_remove = self.peer_ids[peer_to_remove].clone();
244
0
                let state = self
245
0
                    .peers_chains
246
0
                    .remove(&(peer_to_remove, chain_index))
247
0
                    .unwrap_or_else(|| unreachable!());
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE17insert_chain_peers_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE17insert_chain_peers_0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE17insert_chain_peers_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peers_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peers_0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peers_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peers_0CsibGXYHQB8Ea_25json_rpc_general_requests
248
0
                debug_assert!(!matches!(state, PeerChainState::Slot));
249
0
                let _was_removed =
250
0
                    self.peers_chains_by_state
251
0
                        .remove(&(chain_index, state, peer_to_remove));
252
0
                debug_assert!(_was_removed);
253
0
                self.try_clean_up_peer_id(peer_to_remove);
254
0
                Some(peer_id_to_remove)
255
            } else {
256
2
                None
257
            };
258
259
2
            InsertChainPeerResult::Inserted { peer_removed }
260
        } else {
261
0
            InsertChainPeerResult::Duplicate
262
        }
263
2
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE17insert_chain_peerB6_
Line
Count
Source
201
2
    pub fn insert_chain_peer(
202
2
        &mut self,
203
2
        chain: TChainId,
204
2
        peer_id: PeerId,
205
2
        max_peers_per_chain: usize,
206
2
    ) -> InsertChainPeerResult {
207
2
        let peer_id_index = self.get_or_insert_peer_index(&peer_id);
208
2
        let chain_index = self.get_or_insert_chain_index(&chain);
209
210
2
        if let btree_map::Entry::Vacant(entry) =
211
2
            self.peers_chains.entry((peer_id_index, chain_index))
212
        {
213
2
            let peer_to_remove = if self
214
2
                .peers_chains_by_state
215
2
                .range(
216
2
                    (chain_index, PeerChainState::Assignable, usize::MIN)
217
2
                        ..=(chain_index, PeerChainState::Slot, usize::MAX),
218
2
                )
219
2
                .count()
220
2
                >= max_peers_per_chain
221
            {
222
0
                self.peers_chains_by_state
223
0
                    .range(
224
0
                        (chain_index, PeerChainState::Assignable, usize::MIN)
225
0
                            ..(chain_index, PeerChainState::Slot, usize::MIN),
226
0
                    )
227
0
                    .choose(&mut self.randomness)
228
0
                    .map(|(_, _, peer_index)| *peer_index)
229
            } else {
230
2
                None
231
            };
232
233
2
            let _was_inserted = self.peers_chains_by_state.insert((
234
2
                chain_index,
235
2
                PeerChainState::Assignable,
236
2
                peer_id_index,
237
2
            ));
238
2
            debug_assert!(_was_inserted);
239
240
2
            entry.insert(PeerChainState::Assignable);
241
242
2
            let peer_removed = if let Some(
peer_to_remove0
) = peer_to_remove {
243
0
                let peer_id_to_remove = self.peer_ids[peer_to_remove].clone();
244
0
                let state = self
245
0
                    .peers_chains
246
0
                    .remove(&(peer_to_remove, chain_index))
247
0
                    .unwrap_or_else(|| unreachable!());
248
0
                debug_assert!(!matches!(state, PeerChainState::Slot));
249
0
                let _was_removed =
250
0
                    self.peers_chains_by_state
251
0
                        .remove(&(chain_index, state, peer_to_remove));
252
0
                debug_assert!(_was_removed);
253
0
                self.try_clean_up_peer_id(peer_to_remove);
254
0
                Some(peer_id_to_remove)
255
            } else {
256
2
                None
257
            };
258
259
2
            InsertChainPeerResult::Inserted { peer_removed }
260
        } else {
261
0
            InsertChainPeerResult::Duplicate
262
        }
263
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE17insert_chain_peerCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE17insert_chain_peerB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peerCsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peerCsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peerCscDgN54JpMGG_6author
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE17insert_chain_peerCsibGXYHQB8Ea_25json_rpc_general_requests
264
265
    /// Removes a peer-chain associated previously inserted with
266
    /// [`BasicPeeringStrategy::insert_chain_peer`].
267
    ///
268
    /// Has no effect if the peer-chain association didn't exist.
269
    ///
270
    /// If the peer isn't assigned to any chain anymore and doesn't have any connected address,
271
    /// all of its addresses are also removed from the collection.
272
2
    pub fn unassign_slot_and_remove_chain_peer(
273
2
        &mut self,
274
2
        chain: &TChainId,
275
2
        peer_id: &PeerId,
276
2
    ) -> UnassignSlotAndRemoveChainPeer<TInstant> {
277
2
        let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
278
            // If the `PeerId` is unknown, it means it wasn't assigned in the first place.
279
0
            return UnassignSlotAndRemoveChainPeer::NotAssigned;
280
        };
281
282
2
        let Some(&chain_index) = self.chains_indices.get(chain) else {
283
            // If the `TChainId` is unknown, it means the peer wasn't assigned in the first place.
284
0
            return UnassignSlotAndRemoveChainPeer::NotAssigned;
285
        };
286
287
2
        if let Some(state) = self.peers_chains.remove(&(peer_id_index, chain_index)) {
288
2
            let _was_removed =
289
2
                self.peers_chains_by_state
290
2
                    .remove(&(chain_index, state.clone(), peer_id_index));
291
2
            debug_assert!(_was_removed);
292
293
2
            self.try_clean_up_peer_id(peer_id_index);
294
2
            self.try_clean_up_chain(chain_index);
295
2
296
2
            match state {
297
2
                PeerChainState::Assignable => UnassignSlotAndRemoveChainPeer::Assigned {
298
2
                    ban_expiration: None,
299
2
                },
300
0
                PeerChainState::Banned { expires } => UnassignSlotAndRemoveChainPeer::Assigned {
301
0
                    ban_expiration: Some(expires),
302
0
                },
303
0
                PeerChainState::Slot => UnassignSlotAndRemoveChainPeer::HadSlot,
304
            }
305
        } else {
306
0
            UnassignSlotAndRemoveChainPeer::NotAssigned
307
        }
308
2
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE35unassign_slot_and_remove_chain_peerB6_
Line
Count
Source
272
2
    pub fn unassign_slot_and_remove_chain_peer(
273
2
        &mut self,
274
2
        chain: &TChainId,
275
2
        peer_id: &PeerId,
276
2
    ) -> UnassignSlotAndRemoveChainPeer<TInstant> {
277
2
        let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
278
            // If the `PeerId` is unknown, it means it wasn't assigned in the first place.
279
0
            return UnassignSlotAndRemoveChainPeer::NotAssigned;
280
        };
281
282
2
        let Some(&chain_index) = self.chains_indices.get(chain) else {
283
            // If the `TChainId` is unknown, it means the peer wasn't assigned in the first place.
284
0
            return UnassignSlotAndRemoveChainPeer::NotAssigned;
285
        };
286
287
2
        if let Some(state) = self.peers_chains.remove(&(peer_id_index, chain_index)) {
288
2
            let _was_removed =
289
2
                self.peers_chains_by_state
290
2
                    .remove(&(chain_index, state.clone(), peer_id_index));
291
2
            debug_assert!(_was_removed);
292
293
2
            self.try_clean_up_peer_id(peer_id_index);
294
2
            self.try_clean_up_chain(chain_index);
295
2
296
2
            match state {
297
2
                PeerChainState::Assignable => UnassignSlotAndRemoveChainPeer::Assigned {
298
2
                    ban_expiration: None,
299
2
                },
300
0
                PeerChainState::Banned { expires } => UnassignSlotAndRemoveChainPeer::Assigned {
301
0
                    ban_expiration: Some(expires),
302
0
                },
303
0
                PeerChainState::Slot => UnassignSlotAndRemoveChainPeer::HadSlot,
304
            }
305
        } else {
306
0
            UnassignSlotAndRemoveChainPeer::NotAssigned
307
        }
308
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE35unassign_slot_and_remove_chain_peerCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE35unassign_slot_and_remove_chain_peerB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE35unassign_slot_and_remove_chain_peerCsiUjFBJteJ7x_17smoldot_full_node
309
310
    /// Returns the list of all peers that are known to belong to the given chain, in other
311
    /// words peers added through [`BasicPeeringStrategy::insert_chain_peer`].
312
    ///
313
    /// The order of the yielded elements is unspecified.
314
0
    pub fn chain_peers_unordered(
315
0
        &'_ self,
316
0
        chain: &TChainId,
317
0
    ) -> impl Iterator<Item = &'_ PeerId> + '_ {
318
0
        let Some(&chain_index) = self.chains_indices.get(chain) else {
319
            // If the `TChainId` is unknown, it means that it doesn't have any peer.
320
0
            return either::Right(iter::empty());
321
        };
322
323
0
        either::Left(
324
0
            self.peers_chains_by_state
325
0
                .range(
326
0
                    (chain_index, PeerChainState::Assignable, usize::MIN)
327
0
                        ..=(chain_index, PeerChainState::Slot, usize::MAX),
328
0
                )
329
0
                .map(|(_, _, p)| &self.peer_ids[*p]),
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE21chain_peers_unordered0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE21chain_peers_unordered0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE21chain_peers_unordered0B8_
330
0
        )
331
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE21chain_peers_unorderedB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE21chain_peers_unorderedCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE21chain_peers_unorderedB6_
332
333
    /// Inserts a new address for the given peer.
334
    ///
335
    /// If the address wasn't known yet, its number of connections is set to zero.
336
    ///
337
    /// If the peer doesn't belong to any chain (see [`BasicPeeringStrategy::insert_chain_peer`]),
338
    /// then this function has no effect, unless the peer has at least one connected address. This
339
    /// is to avoid accidentally collecting addresses for peers that will never be removed and
340
    /// create a memory leak. For this reason, you most likely want to call
341
    /// [`BasicPeeringStrategy::insert_chain_peer`] before calling this function.
342
    ///
343
    /// A maximum number of addresses that are maintained for this peer must be passed as
344
    /// parameter. If this number is exceeded, an address with zero connections (other than
345
    /// the one passed as parameter) is randomly removed.
346
3
    pub fn insert_address(
347
3
        &mut self,
348
3
        peer_id: &PeerId,
349
3
        address: Vec<u8>,
350
3
        max_addresses: usize,
351
3
    ) -> InsertAddressResult {
352
3
        let Some(&
peer_id_index2
) = self.peer_ids_indices.get(peer_id) else {
353
1
            return InsertAddressResult::UnknownPeer;
354
        };
355
356
2
        match self.insert_address_inner(peer_id_index, address, max_addresses, 0, false) {
357
0
            InsertAddressConnectionsResult::AlreadyKnown => InsertAddressResult::AlreadyKnown,
358
2
            InsertAddressConnectionsResult::Inserted { address_removed } => {
359
2
                InsertAddressResult::Inserted { address_removed }
360
            }
361
        }
362
3
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE14insert_addressB6_
Line
Count
Source
346
3
    pub fn insert_address(
347
3
        &mut self,
348
3
        peer_id: &PeerId,
349
3
        address: Vec<u8>,
350
3
        max_addresses: usize,
351
3
    ) -> InsertAddressResult {
352
3
        let Some(&
peer_id_index2
) = self.peer_ids_indices.get(peer_id) else {
353
1
            return InsertAddressResult::UnknownPeer;
354
        };
355
356
2
        match self.insert_address_inner(peer_id_index, address, max_addresses, 0, false) {
357
0
            InsertAddressConnectionsResult::AlreadyKnown => InsertAddressResult::AlreadyKnown,
358
2
            InsertAddressConnectionsResult::Inserted { address_removed } => {
359
2
                InsertAddressResult::Inserted { address_removed }
360
            }
361
        }
362
3
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE14insert_addressCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE14insert_addressB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE14insert_addressCsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE14insert_addressCsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE14insert_addressCscDgN54JpMGG_6author
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE14insert_addressCsibGXYHQB8Ea_25json_rpc_general_requests
363
364
    /// Increases the number of connections of the given address. If the address isn't known, it
365
    /// is inserted.
366
    ///
367
    /// Contrary to [`BasicPeeringStrategy::insert_address`], the address is inserted anyway if
368
    /// the `PeerId` isn't known.
369
    ///
370
    /// > **Note**: Use this function if you establish a connection and accidentally reach a
371
    /// >           certain [`PeerId`].
372
    ///
373
    /// # Panic
374
    ///
375
    /// Panics if the number of connections is equal to `u32::MAX`.
376
    ///
377
2
    pub fn increase_address_connections(
378
2
        &mut self,
379
2
        peer_id: &PeerId,
380
2
        address: Vec<u8>,
381
2
        max_addresses: usize,
382
2
    ) -> InsertAddressConnectionsResult {
383
2
        let peer_id_index = self.get_or_insert_peer_index(peer_id);
384
2
        self.insert_address_inner(peer_id_index, address, max_addresses, 1, true)
385
2
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE28increase_address_connectionsB6_
Line
Count
Source
377
2
    pub fn increase_address_connections(
378
2
        &mut self,
379
2
        peer_id: &PeerId,
380
2
        address: Vec<u8>,
381
2
        max_addresses: usize,
382
2
    ) -> InsertAddressConnectionsResult {
383
2
        let peer_id_index = self.get_or_insert_peer_index(peer_id);
384
2
        self.insert_address_inner(peer_id_index, address, max_addresses, 1, true)
385
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE28increase_address_connectionsCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE28increase_address_connectionsB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE28increase_address_connectionsCsiUjFBJteJ7x_17smoldot_full_node
386
387
4
    fn insert_address_inner(
388
4
        &mut self,
389
4
        peer_id_index: usize,
390
4
        address: Vec<u8>,
391
4
        max_addresses: usize,
392
4
        initial_num_connections: u32,
393
4
        increase_if_present: bool,
394
4
    ) -> InsertAddressConnectionsResult {
395
4
        match self.addresses.entry((peer_id_index, address.clone())) {
396
4
            btree_map::Entry::Vacant(entry) => {
397
4
                entry.insert(initial_num_connections);
398
399
4
                let address_removed = {
400
4
                    let num_addresses = self
401
4
                        .addresses
402
4
                        .range((peer_id_index, Vec::new())..=(peer_id_index + 1, Vec::new()))
403
4
                        .count();
404
4
405
4
                    if num_addresses >= max_addresses {
406
                        // TODO: is it a good idea to choose the address randomly to remove? maybe there should be a sorting system with best addresses first?
407
0
                        self.addresses
408
0
                            .range((peer_id_index, Vec::new())..=(peer_id_index + 1, Vec::new()))
409
0
                            .filter(|((_, a), n)| **n == 0 && *a != address)
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_inner0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_inner0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE20insert_address_inner0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inner0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inner0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inner0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inner0CsibGXYHQB8Ea_25json_rpc_general_requests
410
0
                            .choose(&mut self.randomness)
411
0
                            .map(|((_, a), _)| a.clone())
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_inners_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_inners_0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE20insert_address_inners_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners_0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners_0CsibGXYHQB8Ea_25json_rpc_general_requests
412
                    } else {
413
4
                        None
414
                    }
415
                };
416
417
4
                if let Some(
address_removed0
) = address_removed.as_ref() {
418
0
                    self.addresses
419
0
                        .remove(&(peer_id_index, address_removed.clone()));
420
4
                }
421
422
4
                InsertAddressConnectionsResult::Inserted { address_removed }
423
            }
424
0
            btree_map::Entry::Occupied(entry) => {
425
0
                let entry = entry.into_mut();
426
0
                if increase_if_present {
427
0
                    *entry = entry
428
0
                        .checked_add(1)
429
0
                        .unwrap_or_else(|| panic!("overflow in number of connections"));
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_inners0_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_inners0_0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE20insert_address_inners0_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners0_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners0_0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners0_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_inners0_0CsibGXYHQB8Ea_25json_rpc_general_requests
430
0
                }
431
432
0
                InsertAddressConnectionsResult::AlreadyKnown
433
            }
434
        }
435
4
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_innerB6_
Line
Count
Source
387
4
    fn insert_address_inner(
388
4
        &mut self,
389
4
        peer_id_index: usize,
390
4
        address: Vec<u8>,
391
4
        max_addresses: usize,
392
4
        initial_num_connections: u32,
393
4
        increase_if_present: bool,
394
4
    ) -> InsertAddressConnectionsResult {
395
4
        match self.addresses.entry((peer_id_index, address.clone())) {
396
4
            btree_map::Entry::Vacant(entry) => {
397
4
                entry.insert(initial_num_connections);
398
399
4
                let address_removed = {
400
4
                    let num_addresses = self
401
4
                        .addresses
402
4
                        .range((peer_id_index, Vec::new())..=(peer_id_index + 1, Vec::new()))
403
4
                        .count();
404
4
405
4
                    if num_addresses >= max_addresses {
406
                        // TODO: is it a good idea to choose the address randomly to remove? maybe there should be a sorting system with best addresses first?
407
0
                        self.addresses
408
0
                            .range((peer_id_index, Vec::new())..=(peer_id_index + 1, Vec::new()))
409
0
                            .filter(|((_, a), n)| **n == 0 && *a != address)
410
0
                            .choose(&mut self.randomness)
411
0
                            .map(|((_, a), _)| a.clone())
412
                    } else {
413
4
                        None
414
                    }
415
                };
416
417
4
                if let Some(
address_removed0
) = address_removed.as_ref() {
418
0
                    self.addresses
419
0
                        .remove(&(peer_id_index, address_removed.clone()));
420
4
                }
421
422
4
                InsertAddressConnectionsResult::Inserted { address_removed }
423
            }
424
0
            btree_map::Entry::Occupied(entry) => {
425
0
                let entry = entry.into_mut();
426
0
                if increase_if_present {
427
0
                    *entry = entry
428
0
                        .checked_add(1)
429
0
                        .unwrap_or_else(|| panic!("overflow in number of connections"));
430
0
                }
431
432
0
                InsertAddressConnectionsResult::AlreadyKnown
433
            }
434
        }
435
4
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20insert_address_innerCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE20insert_address_innerB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_innerCsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_innerCsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_innerCscDgN54JpMGG_6author
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20insert_address_innerCsibGXYHQB8Ea_25json_rpc_general_requests
436
437
    /// Returns the list of all addresses that have been inserted for the given peer.
438
7
    pub fn peer_addresses(&'_ self, peer_id: &PeerId) -> impl Iterator<Item = &'_ [u8]> + '_ {
439
7
        let Some(&
peer_id_index4
) = self.peer_ids_indices.get(peer_id) else {
440
            // If the `PeerId` is unknown, it means it doesn't have any address.
441
3
            return either::Right(iter::empty());
442
        };
443
444
4
        either::Left(
445
4
            self.addresses
446
4
                .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
447
6
                .map(|((_, a), _)| &a[..]),
_RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE14peer_addresses0B8_
Line
Count
Source
447
6
                .map(|((_, a), _)| &a[..]),
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE14peer_addresses0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE14peer_addresses0B8_
448
4
        )
449
7
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE14peer_addressesB6_
Line
Count
Source
438
7
    pub fn peer_addresses(&'_ self, peer_id: &PeerId) -> impl Iterator<Item = &'_ [u8]> + '_ {
439
7
        let Some(&
peer_id_index4
) = self.peer_ids_indices.get(peer_id) else {
440
            // If the `PeerId` is unknown, it means it doesn't have any address.
441
3
            return either::Right(iter::empty());
442
        };
443
444
4
        either::Left(
445
4
            self.addresses
446
4
                .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
447
4
                .map(|((_, a), _)| &a[..]),
448
4
        )
449
7
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE14peer_addressesCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE14peer_addressesB6_
450
451
    /// Chooses a [`PeerId`] that is known to belong to the given chain, that is not banned, and
452
    /// that doesn't have a slot assigned to it.
453
    ///
454
    /// A `TInstant` must be provided in order to determine whether past bans have expired.
455
    ///
456
    /// If multiple peers can be assigned a slot, the one returned is chosen randomly. Calling
457
    /// this function multiple times might return different peers.
458
    /// For this reason, this function requires `&mut self`.
459
    ///
460
    /// Note that this function might return a peer for which no address is present. While this is
461
    /// often not desirable, it is preferable to keep the API simple and straight-forward rather
462
    /// than try to be smart about function behaviours.
463
147
    pub fn pick_assignable_peer(
464
147
        &'_ mut self,
465
147
        chain: &TChainId,
466
147
        now: &TInstant,
467
147
    ) -> AssignablePeer<'_, TInstant> {
468
147
        let Some(&
chain_index0
) = self.chains_indices.get(chain) else {
469
147
            return AssignablePeer::NoPeer;
470
        };
471
472
0
        if let Some((_, _, peer_id_index)) = self
473
0
            .peers_chains_by_state
474
0
            .range(
475
0
                (chain_index, PeerChainState::Assignable, usize::MIN)
476
0
                    ..=(
477
0
                        chain_index,
478
0
                        PeerChainState::Banned {
479
0
                            expires: now.clone(),
480
0
                        },
481
0
                        usize::MAX,
482
0
                    ),
483
0
            )
484
0
            .choose(&mut self.randomness)
485
        {
486
0
            return AssignablePeer::Assignable(&self.peer_ids[*peer_id_index]);
487
0
        }
488
489
0
        if let Some((_, state, _)) = self
490
0
            .peers_chains_by_state
491
0
            .range((
492
0
                ops::Bound::Excluded((
493
0
                    chain_index,
494
0
                    PeerChainState::Banned {
495
0
                        expires: now.clone(),
496
0
                    },
497
0
                    usize::MAX,
498
0
                )),
499
0
                ops::Bound::Excluded((chain_index, PeerChainState::Slot, usize::MIN)),
500
0
            ))
501
0
            .next()
502
        {
503
0
            let PeerChainState::Banned { expires } = state else {
504
0
                unreachable!()
505
            };
506
0
            AssignablePeer::AllPeersBanned {
507
0
                next_unban: expires,
508
0
            }
509
        } else {
510
0
            AssignablePeer::NoPeer
511
        }
512
147
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE20pick_assignable_peerB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20pick_assignable_peerCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE20pick_assignable_peerB6_
_RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20pick_assignable_peerCsiUjFBJteJ7x_17smoldot_full_node
Line
Count
Source
463
147
    pub fn pick_assignable_peer(
464
147
        &'_ mut self,
465
147
        chain: &TChainId,
466
147
        now: &TInstant,
467
147
    ) -> AssignablePeer<'_, TInstant> {
468
147
        let Some(&
chain_index0
) = self.chains_indices.get(chain) else {
469
147
            return AssignablePeer::NoPeer;
470
        };
471
472
0
        if let Some((_, _, peer_id_index)) = self
473
0
            .peers_chains_by_state
474
0
            .range(
475
0
                (chain_index, PeerChainState::Assignable, usize::MIN)
476
0
                    ..=(
477
0
                        chain_index,
478
0
                        PeerChainState::Banned {
479
0
                            expires: now.clone(),
480
0
                        },
481
0
                        usize::MAX,
482
0
                    ),
483
0
            )
484
0
            .choose(&mut self.randomness)
485
        {
486
0
            return AssignablePeer::Assignable(&self.peer_ids[*peer_id_index]);
487
0
        }
488
489
0
        if let Some((_, state, _)) = self
490
0
            .peers_chains_by_state
491
0
            .range((
492
0
                ops::Bound::Excluded((
493
0
                    chain_index,
494
0
                    PeerChainState::Banned {
495
0
                        expires: now.clone(),
496
0
                    },
497
0
                    usize::MAX,
498
0
                )),
499
0
                ops::Bound::Excluded((chain_index, PeerChainState::Slot, usize::MIN)),
500
0
            ))
501
0
            .next()
502
        {
503
0
            let PeerChainState::Banned { expires } = state else {
504
0
                unreachable!()
505
            };
506
0
            AssignablePeer::AllPeersBanned {
507
0
                next_unban: expires,
508
0
            }
509
        } else {
510
0
            AssignablePeer::NoPeer
511
        }
512
147
    }
513
514
    /// Assigns a slot to the given peer on the given chain.
515
    ///
516
    /// Acts as an implicit call to [`BasicPeeringStrategy::insert_chain_peer`].
517
    ///
518
    /// A slot is assigned even if the peer is banned. API users that call this function are
519
    /// expected to be aware of that.
520
0
    pub fn assign_slot(&'_ mut self, chain: &TChainId, peer_id: &PeerId) {
521
0
        let peer_id_index = self.get_or_insert_peer_index(peer_id);
522
0
        let chain_index = self.get_or_insert_chain_index(chain);
523
0
524
0
        match self.peers_chains.entry((peer_id_index, chain_index)) {
525
0
            btree_map::Entry::Occupied(e) => {
526
0
                let _was_removed = self.peers_chains_by_state.remove(&(
527
0
                    chain_index,
528
0
                    e.get().clone(),
529
0
                    peer_id_index,
530
0
                ));
531
0
                debug_assert!(_was_removed);
532
0
                *e.into_mut() = PeerChainState::Slot;
533
            }
534
0
            btree_map::Entry::Vacant(e) => {
535
0
                e.insert(PeerChainState::Slot);
536
0
            }
537
        }
538
539
0
        let _was_inserted =
540
0
            self.peers_chains_by_state
541
0
                .insert((chain_index, PeerChainState::Slot, peer_id_index));
542
0
        debug_assert!(_was_inserted);
543
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE11assign_slotB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE11assign_slotCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE11assign_slotB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE11assign_slotCsiUjFBJteJ7x_17smoldot_full_node
544
545
    /// Unassign the slot that has been assigned to the given peer and bans the peer, preventing
546
    /// it from being assigned a slot on this chain for a certain amount of time.
547
    ///
548
    /// Has no effect if the peer isn't assigned to the given chain.
549
    ///
550
    /// If the peer was already banned, the new ban expiration is `max(existing_ban, when_unban)`.
551
    ///
552
    /// Returns what this function did.
553
0
    pub fn unassign_slot_and_ban(
554
0
        &mut self,
555
0
        chain: &TChainId,
556
0
        peer_id: &PeerId,
557
0
        when_unban: TInstant,
558
0
    ) -> UnassignSlotAndBan<TInstant> {
559
0
        let (Some(&peer_id_index), Some(&chain_index)) = (
560
0
            self.peer_ids_indices.get(peer_id),
561
0
            self.chains_indices.get(chain),
562
        ) else {
563
0
            return UnassignSlotAndBan::NotAssigned;
564
        };
565
566
0
        if let Some(state) = self.peers_chains.get_mut(&(peer_id_index, chain_index)) {
567
0
            let return_value = match state {
568
0
                PeerChainState::Banned { expires } if *expires >= when_unban => {
569
0
                    // Ban is already long enough. Nothing to do.
570
0
                    return UnassignSlotAndBan::AlreadyBanned {
571
0
                        when_unban: expires.clone(),
572
0
                        ban_extended: false,
573
0
                    };
574
                }
575
0
                PeerChainState::Banned { .. } => UnassignSlotAndBan::AlreadyBanned {
576
0
                    when_unban: when_unban.clone(),
577
0
                    ban_extended: true,
578
0
                },
579
0
                PeerChainState::Assignable => UnassignSlotAndBan::Banned { had_slot: false },
580
0
                PeerChainState::Slot => UnassignSlotAndBan::Banned { had_slot: true },
581
            };
582
583
0
            let _was_in =
584
0
                self.peers_chains_by_state
585
0
                    .remove(&(chain_index, state.clone(), peer_id_index));
586
0
            debug_assert!(_was_in);
587
588
0
            *state = PeerChainState::Banned {
589
0
                expires: when_unban,
590
0
            };
591
0
592
0
            let _was_inserted =
593
0
                self.peers_chains_by_state
594
0
                    .insert((chain_index, state.clone(), peer_id_index));
595
0
            debug_assert!(_was_inserted);
596
597
0
            return_value
598
        } else {
599
0
            UnassignSlotAndBan::NotAssigned
600
        }
601
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE21unassign_slot_and_banB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE21unassign_slot_and_banCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE21unassign_slot_and_banB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE21unassign_slot_and_banCsiUjFBJteJ7x_17smoldot_full_node
602
603
    /// Unassigns all the slots that have been assigned to the given peer and bans the peer,
604
    /// preventing it from being assigned a slot for all of the chains it had a slot on for a
605
    /// certain amount of time.
606
    ///
607
    /// Has no effect on chains the peer isn't assigned to.
608
    ///
609
    /// If the peer was already banned, the new ban expiration is `max(existing_ban, when_unban)`.
610
    ///
611
    /// Returns an iterator to the list of chains where the peer is now banned, and the details
612
    /// of what has happened.
613
    ///
614
    /// > **Note**: This function is a shortcut for calling
615
    /// >           [`BasicPeeringStrategy::unassign_slot_and_ban`] for all existing chains.
616
0
    pub fn unassign_slots_and_ban(
617
0
        &mut self,
618
0
        peer_id: &PeerId,
619
0
        when_unban: TInstant,
620
0
    ) -> UnassignSlotsAndBanIter<TChainId, TInstant> {
621
0
        let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
622
0
            return UnassignSlotsAndBanIter {
623
0
                chains: &self.chains,
624
0
                peers_chains_by_state: &mut self.peers_chains_by_state,
625
0
                inner_iter: None,
626
0
                peer_id_index: 0,
627
0
                when_unban,
628
0
            };
629
        };
630
631
0
        UnassignSlotsAndBanIter {
632
0
            chains: &self.chains,
633
0
            peers_chains_by_state: &mut self.peers_chains_by_state,
634
0
            inner_iter: Some(
635
0
                self.peers_chains
636
0
                    .range_mut((peer_id_index, usize::MIN)..=(peer_id_index, usize::MAX))
637
0
                    .fuse(),
638
0
            ),
639
0
            peer_id_index,
640
0
            when_unban,
641
0
        }
642
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE22unassign_slots_and_banB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE22unassign_slots_and_banCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE22unassign_slots_and_banB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE22unassign_slots_and_banCsiUjFBJteJ7x_17smoldot_full_node
643
644
    /// Picks an address from the list with zero connections, and sets the number of connections
645
    /// to one. Returns `None` if no such address is available.
646
0
    pub fn pick_address_and_add_connection(&mut self, peer_id: &PeerId) -> Option<&[u8]> {
647
0
        let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
648
            // If the `PeerId` is unknown, it means it doesn't have any address.
649
0
            return None;
650
        };
651
652
        // TODO: could be optimized further by removing filter() and adjusting the set
653
0
        if let Some(((_, address), num_connections)) = self
654
0
            .addresses
655
0
            .range_mut((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
656
0
            .filter(|(_, num_connections)| **num_connections == 0)
Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE31pick_address_and_add_connection0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE31pick_address_and_add_connection0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE31pick_address_and_add_connection0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE31pick_address_and_add_connection0CsiUjFBJteJ7x_17smoldot_full_node
657
0
            .choose(&mut self.randomness)
658
        {
659
0
            *num_connections = 1;
660
0
            return Some(address);
661
0
        }
662
0
663
0
        None
664
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE31pick_address_and_add_connectionB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE31pick_address_and_add_connectionCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE31pick_address_and_add_connectionB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE31pick_address_and_add_connectionCsiUjFBJteJ7x_17smoldot_full_node
665
666
    /// Removes one connection from the given address.
667
    ///
668
    /// Returns an error if the address isn't known to the data structure, or if there was no
669
    /// connection.
670
1
    pub fn decrease_address_connections(
671
1
        &mut self,
672
1
        peer_id: &PeerId,
673
1
        address: &[u8],
674
1
    ) -> Result<(), DecreaseAddressConnectionsError> {
675
1
        self.decrease_address_connections_inner(peer_id, address, false)
676
1
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE28decrease_address_connectionsB6_
Line
Count
Source
670
1
    pub fn decrease_address_connections(
671
1
        &mut self,
672
1
        peer_id: &PeerId,
673
1
        address: &[u8],
674
1
    ) -> Result<(), DecreaseAddressConnectionsError> {
675
1
        self.decrease_address_connections_inner(peer_id, address, false)
676
1
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE28decrease_address_connectionsCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE28decrease_address_connectionsB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE28decrease_address_connectionsCsiUjFBJteJ7x_17smoldot_full_node
677
678
    /// Removes one connection from the given address. If this decreases the number of connections
679
    /// from one to zero, the address is removed entirely.
680
    ///
681
    /// Returns an error if the address isn't known to the data structure, or if there was no
682
    /// connection.
683
0
    pub fn decrease_address_connections_and_remove_if_zero(
684
0
        &mut self,
685
0
        peer_id: &PeerId,
686
0
        address: &[u8],
687
0
    ) -> Result<(), DecreaseAddressConnectionsError> {
688
0
        self.decrease_address_connections_inner(peer_id, address, true)
689
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE47decrease_address_connections_and_remove_if_zeroB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE47decrease_address_connections_and_remove_if_zeroCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE47decrease_address_connections_and_remove_if_zeroB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE47decrease_address_connections_and_remove_if_zeroCsiUjFBJteJ7x_17smoldot_full_node
690
691
1
    fn decrease_address_connections_inner(
692
1
        &mut self,
693
1
        peer_id: &PeerId,
694
1
        address: &[u8],
695
1
        remove_if_reaches_zero: bool,
696
1
    ) -> Result<(), DecreaseAddressConnectionsError> {
697
1
        let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
698
            // If the `PeerId` is unknown, it means it doesn't have any address.
699
0
            return Err(DecreaseAddressConnectionsError::UnknownAddress);
700
        };
701
702
1
        let Some(num_connections) = self.addresses.get_mut(&(peer_id_index, address.to_owned()))
703
        else {
704
0
            return Err(DecreaseAddressConnectionsError::UnknownAddress);
705
        };
706
707
1
        if *num_connections == 0 {
708
0
            return Err(DecreaseAddressConnectionsError::NotConnected);
709
1
        }
710
1
711
1
        *num_connections -= 1;
712
1
713
1
        if *num_connections != 0 {
714
0
            return Ok(());
715
1
        }
716
1
717
1
        if remove_if_reaches_zero {
718
0
            self.addresses.remove(&(peer_id_index, address.to_owned()));
719
1
        }
720
721
1
        self.try_clean_up_peer_id(peer_id_index);
722
1
        Ok(())
723
1
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE34decrease_address_connections_innerB6_
Line
Count
Source
691
1
    fn decrease_address_connections_inner(
692
1
        &mut self,
693
1
        peer_id: &PeerId,
694
1
        address: &[u8],
695
1
        remove_if_reaches_zero: bool,
696
1
    ) -> Result<(), DecreaseAddressConnectionsError> {
697
1
        let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
698
            // If the `PeerId` is unknown, it means it doesn't have any address.
699
0
            return Err(DecreaseAddressConnectionsError::UnknownAddress);
700
        };
701
702
1
        let Some(num_connections) = self.addresses.get_mut(&(peer_id_index, address.to_owned()))
703
        else {
704
0
            return Err(DecreaseAddressConnectionsError::UnknownAddress);
705
        };
706
707
1
        if *num_connections == 0 {
708
0
            return Err(DecreaseAddressConnectionsError::NotConnected);
709
1
        }
710
1
711
1
        *num_connections -= 1;
712
1
713
1
        if *num_connections != 0 {
714
0
            return Ok(());
715
1
        }
716
1
717
1
        if remove_if_reaches_zero {
718
0
            self.addresses.remove(&(peer_id_index, address.to_owned()));
719
1
        }
720
721
1
        self.try_clean_up_peer_id(peer_id_index);
722
1
        Ok(())
723
1
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE34decrease_address_connections_innerCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE34decrease_address_connections_innerB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE34decrease_address_connections_innerCsiUjFBJteJ7x_17smoldot_full_node
724
725
    /// Finds the index of the given `TChainId` in [`BasicPeeringStrategy::chains`], or inserts
726
    /// one if there is none.
727
2
    fn get_or_insert_chain_index(&mut self, chain: &TChainId) -> usize {
728
2
        debug_assert_eq!(self.chains.len(), self.chains_indices.len());
729
730
2
        match self.chains_indices.raw_entry_mut().from_key(chain) {
731
0
            hashbrown::hash_map::RawEntryMut::Occupied(occupied_entry) => *occupied_entry.get(),
732
2
            hashbrown::hash_map::RawEntryMut::Vacant(vacant_entry) => {
733
2
                let idx = self.chains.insert(chain.clone());
734
2
                vacant_entry.insert(chain.clone(), idx);
735
2
                idx
736
            }
737
        }
738
2
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE25get_or_insert_chain_indexB6_
Line
Count
Source
727
2
    fn get_or_insert_chain_index(&mut self, chain: &TChainId) -> usize {
728
2
        debug_assert_eq!(self.chains.len(), self.chains_indices.len());
729
730
2
        match self.chains_indices.raw_entry_mut().from_key(chain) {
731
0
            hashbrown::hash_map::RawEntryMut::Occupied(occupied_entry) => *occupied_entry.get(),
732
2
            hashbrown::hash_map::RawEntryMut::Vacant(vacant_entry) => {
733
2
                let idx = self.chains.insert(chain.clone());
734
2
                vacant_entry.insert(chain.clone(), idx);
735
2
                idx
736
            }
737
        }
738
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE25get_or_insert_chain_indexCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE25get_or_insert_chain_indexB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE25get_or_insert_chain_indexCsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE25get_or_insert_chain_indexCsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE25get_or_insert_chain_indexCscDgN54JpMGG_6author
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE25get_or_insert_chain_indexCsibGXYHQB8Ea_25json_rpc_general_requests
739
740
    /// Check if the given `TChainId` is still used within the collection. If no, removes it from
741
    /// [`BasicPeeringStrategy::chains`].
742
2
    fn try_clean_up_chain(&mut self, chain_index: usize) {
743
2
        if self
744
2
            .peers_chains_by_state
745
2
            .range(
746
2
                (chain_index, PeerChainState::Assignable, usize::MIN)
747
2
                    ..=(chain_index, PeerChainState::Slot, usize::MAX),
748
2
            )
749
2
            .next()
750
2
            .is_some()
751
        {
752
0
            return;
753
2
        }
754
2
755
2
        // Chain is unused. We can remove it.
756
2
        let chain_id = self.chains.remove(chain_index);
757
2
        let _was_in = self.chains_indices.remove(&chain_id);
758
2
        debug_assert_eq!(_was_in, Some(chain_index));
759
2
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE18try_clean_up_chainB6_
Line
Count
Source
742
2
    fn try_clean_up_chain(&mut self, chain_index: usize) {
743
2
        if self
744
2
            .peers_chains_by_state
745
2
            .range(
746
2
                (chain_index, PeerChainState::Assignable, usize::MIN)
747
2
                    ..=(chain_index, PeerChainState::Slot, usize::MAX),
748
2
            )
749
2
            .next()
750
2
            .is_some()
751
        {
752
0
            return;
753
2
        }
754
2
755
2
        // Chain is unused. We can remove it.
756
2
        let chain_id = self.chains.remove(chain_index);
757
2
        let _was_in = self.chains_indices.remove(&chain_id);
758
2
        debug_assert_eq!(_was_in, Some(chain_index));
759
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE18try_clean_up_chainCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE18try_clean_up_chainB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE18try_clean_up_chainCsiUjFBJteJ7x_17smoldot_full_node
760
761
    /// Finds the index of the given [`PeerId`] in [`BasicPeeringStrategy::peer_ids`], or inserts
762
    /// one if there is none.
763
4
    fn get_or_insert_peer_index(&mut self, peer_id: &PeerId) -> usize {
764
4
        debug_assert_eq!(self.peer_ids.len(), self.peer_ids_indices.len());
765
766
4
        match self.peer_ids_indices.raw_entry_mut().from_key(peer_id) {
767
1
            hashbrown::hash_map::RawEntryMut::Occupied(occupied_entry) => *occupied_entry.get(),
768
3
            hashbrown::hash_map::RawEntryMut::Vacant(vacant_entry) => {
769
3
                let idx = self.peer_ids.insert(peer_id.clone());
770
3
                vacant_entry.insert(peer_id.clone(), idx);
771
3
                idx
772
            }
773
        }
774
4
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE24get_or_insert_peer_indexB6_
Line
Count
Source
763
4
    fn get_or_insert_peer_index(&mut self, peer_id: &PeerId) -> usize {
764
4
        debug_assert_eq!(self.peer_ids.len(), self.peer_ids_indices.len());
765
766
4
        match self.peer_ids_indices.raw_entry_mut().from_key(peer_id) {
767
1
            hashbrown::hash_map::RawEntryMut::Occupied(occupied_entry) => *occupied_entry.get(),
768
3
            hashbrown::hash_map::RawEntryMut::Vacant(vacant_entry) => {
769
3
                let idx = self.peer_ids.insert(peer_id.clone());
770
3
                vacant_entry.insert(peer_id.clone(), idx);
771
3
                idx
772
            }
773
        }
774
4
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE24get_or_insert_peer_indexCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE24get_or_insert_peer_indexB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE24get_or_insert_peer_indexCsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE24get_or_insert_peer_indexCsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE24get_or_insert_peer_indexCscDgN54JpMGG_6author
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE24get_or_insert_peer_indexCsibGXYHQB8Ea_25json_rpc_general_requests
775
776
    /// Check if the given [`PeerId`] is still used within the collection. If no, removes it from
777
    /// [`BasicPeeringStrategy::peer_ids`].
778
3
    fn try_clean_up_peer_id(&mut self, peer_id_index: usize) {
779
3
        if self
780
3
            .peers_chains
781
3
            .range((peer_id_index, usize::MIN)..=(peer_id_index, usize::MAX))
782
3
            .next()
783
3
            .is_some()
784
        {
785
0
            return;
786
3
        }
787
3
788
3
        if self
789
3
            .addresses
790
3
            .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
791
4
            .any(|(_, num_connections)| *num_connections >= 1
)3
_RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20try_clean_up_peer_id0B8_
Line
Count
Source
791
4
            .any(|(_, num_connections)| *num_connections >= 1)
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20try_clean_up_peer_id0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE20try_clean_up_peer_id0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_id0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_id0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_id0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_id0CsibGXYHQB8Ea_25json_rpc_general_requests
792
        {
793
1
            return;
794
2
        }
795
2
796
2
        // PeerId is unused. We can remove it.
797
2
        let peer_id = self.peer_ids.remove(peer_id_index);
798
2
        let _was_in = self.peer_ids_indices.remove(&peer_id);
799
2
        debug_assert_eq!(_was_in, Some(peer_id_index));
800
3
        for address in self
801
2
            .addresses
802
2
            .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
803
3
            .map(|((_, a), _)| a.clone())
_RNCNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20try_clean_up_peer_ids_0B8_
Line
Count
Source
803
3
            .map(|((_, a), _)| a.clone())
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20try_clean_up_peer_ids_0CsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyppE20try_clean_up_peer_ids_0B8_
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_ids_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_ids_0CsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_ids_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_20BasicPeeringStrategyNtNtB6_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_ids_0CsibGXYHQB8Ea_25json_rpc_general_requests
804
2
            .collect::<Vec<_>>()
805
        {
806
3
            let _was_removed = self.addresses.remove(&(peer_id_index, address));
807
3
            debug_assert!(_was_removed.is_some());
808
        }
809
3
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategymNtNtCsaYZPK01V26L_4core4time8DurationE20try_clean_up_peer_idB6_
Line
Count
Source
778
3
    fn try_clean_up_peer_id(&mut self, peer_id_index: usize) {
779
3
        if self
780
3
            .peers_chains
781
3
            .range((peer_id_index, usize::MIN)..=(peer_id_index, usize::MAX))
782
3
            .next()
783
3
            .is_some()
784
        {
785
0
            return;
786
3
        }
787
3
788
3
        if self
789
3
            .addresses
790
3
            .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
791
3
            .any(|(_, num_connections)| *num_connections >= 1)
792
        {
793
1
            return;
794
2
        }
795
2
796
2
        // PeerId is unused. We can remove it.
797
2
        let peer_id = self.peer_ids.remove(peer_id_index);
798
2
        let _was_in = self.peer_ids_indices.remove(&peer_id);
799
2
        debug_assert_eq!(_was_in, Some(peer_id_index));
800
3
        for address in self
801
2
            .addresses
802
2
            .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
803
2
            .map(|((_, a), _)| a.clone())
804
2
            .collect::<Vec<_>>()
805
        {
806
3
            let _was_removed = self.addresses.remove(&(peer_id_index, address));
807
3
            debug_assert!(_was_removed.is_some());
808
        }
809
3
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationE20try_clean_up_peer_idCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyppE20try_clean_up_peer_idB6_
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_idCsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_idCsiUjFBJteJ7x_17smoldot_full_node
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_idCscDgN54JpMGG_6author
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB2_20BasicPeeringStrategyNtNtB4_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantE20try_clean_up_peer_idCsibGXYHQB8Ea_25json_rpc_general_requests
810
}
811
812
/// See [`BasicPeeringStrategy::decrease_address_connections`].
813
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXsc_NtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyNtB5_31DecreaseAddressConnectionsErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXsc_NtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyNtB5_31DecreaseAddressConnectionsErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
814
pub enum DecreaseAddressConnectionsError {
815
    /// Address isn't known to the collection.
816
    UnknownAddress,
817
    /// The address didn't have any connection.
818
    NotConnected,
819
}
820
821
/// See [`BasicPeeringStrategy::pick_assignable_peer`].
822
pub enum AssignablePeer<'a, TInstant> {
823
    /// An assignal peer was found. Note that the peer wasn't assigned yet.
824
    Assignable(&'a PeerId),
825
    /// No peer was found as all known un-assigned peers are currently in the "banned" state.
826
    AllPeersBanned {
827
        /// Instant when the first peer will be unbanned.
828
        next_unban: &'a TInstant,
829
    },
830
    /// No un-assigned peer was found.
831
    NoPeer,
832
}
833
834
/// See [`BasicPeeringStrategy::insert_chain_peer`].
835
pub enum InsertChainPeerResult {
836
    /// Peer-chain association has been successfully inserted.
837
    Inserted {
838
        /// If the maximum number of peers is reached, an old peer might have been removed. If so,
839
        /// this contains the peer.
840
        peer_removed: Option<PeerId>,
841
    },
842
    /// Peer-chain association was already inserted.
843
    Duplicate,
844
}
845
846
/// See [`BasicPeeringStrategy::insert_address`].
847
pub enum InsertAddressResult {
848
    /// Address has been successfully inserted.
849
    Inserted {
850
        /// If the maximum number of addresses is reached, an old address might have been
851
        /// removed. If so, this contains the address.
852
        address_removed: Option<Vec<u8>>,
853
    },
854
    /// Address was already known.
855
    AlreadyKnown,
856
    /// The peer isn't associated to any chain, and as such the address was not inserted.
857
    UnknownPeer,
858
}
859
860
/// See [`BasicPeeringStrategy::increase_address_connections`].
861
pub enum InsertAddressConnectionsResult {
862
    /// Address has been inserted.
863
    Inserted {
864
        /// If the maximum number of addresses is reached, an old address might have been
865
        /// removed. If so, this contains the address.
866
        address_removed: Option<Vec<u8>>,
867
    },
868
    /// Address was already known.
869
    AlreadyKnown,
870
}
871
872
/// See [`BasicPeeringStrategy::unassign_slot_and_ban`].
873
pub enum UnassignSlotAndBan<TInstant> {
874
    /// Peer wasn't assigned to the given chain.
875
    NotAssigned,
876
    /// Peer was already banned.
877
    AlreadyBanned {
878
        /// When the peer is unbanned.
879
        when_unban: TInstant,
880
        /// `true` if the ban has been extended, in other words if the value of `when_unban` was
881
        /// superior to the existing ban.
882
        ban_extended: bool,
883
    },
884
    /// Peer wasn't banned and is now banned.
885
    Banned {
886
        /// `true` if the peer had a slot on the chain.
887
        had_slot: bool,
888
    },
889
}
890
891
impl<TInstant> UnassignSlotAndBan<TInstant> {
892
    /// Returns `true` for [`UnassignSlotAndBan::Banned`] where `had_slot` is `true`.
893
0
    pub fn had_slot(&self) -> bool {
894
0
        matches!(self, UnassignSlotAndBan::Banned { had_slot: true })
895
0
    }
Unexecuted instantiation: _RNvMs_NtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategyINtB4_18UnassignSlotAndBanpE8had_slotB8_
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB4_18UnassignSlotAndBanpE8had_slotB8_
896
}
897
898
/// See [`BasicPeeringStrategy::unassign_slot_and_remove_chain_peer`].
899
pub enum UnassignSlotAndRemoveChainPeer<TInstant> {
900
    /// Peer wasn't assigned to the given chain.
901
    NotAssigned,
902
    /// Peer was assigned to the given chain but didn't have a slot or was banned.
903
    Assigned {
904
        /// `Some` if the peer was banned. Contains the ban expiration.
905
        ban_expiration: Option<TInstant>,
906
    },
907
    /// Peer was assigned to the given chain and had a slot.
908
    HadSlot,
909
}
910
911
/// See [`BasicPeeringStrategy::unassign_slots_and_ban`].
912
pub struct UnassignSlotsAndBanIter<'a, TChainId, TInstant>
913
where
914
    TInstant: PartialOrd + Ord + Eq + Clone,
915
{
916
    /// Same field as in [`BasicPeeringStrategy`].
917
    chains: &'a slab::Slab<TChainId>,
918
    /// Same field as in [`BasicPeeringStrategy`].
919
    peers_chains_by_state: &'a mut BTreeSet<(usize, PeerChainState<TInstant>, usize)>,
920
    /// Iterator within [`BasicPeeringStrategy::peers_chains`].
921
    inner_iter:
922
        Option<iter::Fuse<btree_map::RangeMut<'a, (usize, usize), PeerChainState<TInstant>>>>,
923
    /// Parameter passed to [`BasicPeeringStrategy::unassign_slots_and_ban`]. Dummy value when
924
    /// [`UnassignSlotsAndBanIter::inner_iter`] is `None`.
925
    peer_id_index: usize,
926
    /// Parameter passed to [`BasicPeeringStrategy::unassign_slots_and_ban`].
927
    when_unban: TInstant,
928
}
929
930
/// See [`BasicPeeringStrategy::unassign_slots_and_ban`].
931
pub enum UnassignSlotsAndBan<TInstant> {
932
    /// Peer was already banned.
933
    AlreadyBanned {
934
        /// When the peer is unbanned.
935
        when_unban: TInstant,
936
        /// `true` if the ban has been extended, in other words if the value of `when_unban` was
937
        /// superior to the existing ban.
938
        ban_extended: bool,
939
    },
940
    /// Peer wasn't banned and is now banned.
941
    Banned {
942
        /// `true` if the peer had a slot on the chain.
943
        had_slot: bool,
944
    },
945
}
946
947
impl<'a, TChainId, TInstant> Iterator for UnassignSlotsAndBanIter<'a, TChainId, TInstant>
948
where
949
    TInstant: PartialOrd + Ord + Eq + Clone,
950
{
951
    type Item = (&'a TChainId, UnassignSlotsAndBan<TInstant>);
952
953
0
    fn next(&mut self) -> Option<Self::Item> {
954
0
        let Some(inner_iter) = self.inner_iter.as_mut() else {
955
0
            return None;
956
        };
957
958
        loop {
959
0
            let Some((&(_, chain_index), state)) = inner_iter.next() else {
960
0
                return None;
961
            };
962
963
0
            let return_value = match state {
964
0
                PeerChainState::Banned { expires } if *expires >= self.when_unban => {
965
0
                    // Ban is already long enough. Nothing to do.
966
0
                    return Some((
967
0
                        &self.chains[chain_index],
968
0
                        UnassignSlotsAndBan::AlreadyBanned {
969
0
                            when_unban: expires.clone(),
970
0
                            ban_extended: false,
971
0
                        },
972
0
                    ));
973
                }
974
0
                PeerChainState::Banned { .. } => UnassignSlotsAndBan::AlreadyBanned {
975
0
                    when_unban: self.when_unban.clone(),
976
0
                    ban_extended: true,
977
0
                },
978
0
                PeerChainState::Assignable => UnassignSlotsAndBan::Banned { had_slot: false },
979
0
                PeerChainState::Slot => UnassignSlotsAndBan::Banned { had_slot: true },
980
            };
981
982
0
            let _was_in = self.peers_chains_by_state.remove(&(
983
0
                chain_index,
984
0
                state.clone(),
985
0
                self.peer_id_index,
986
0
            ));
987
0
            debug_assert!(_was_in);
988
989
0
            *state = PeerChainState::Banned {
990
0
                expires: self.when_unban.clone(),
991
0
            };
992
0
993
0
            let _was_inserted =
994
0
                self.peers_chains_by_state
995
0
                    .insert((chain_index, state.clone(), self.peer_id_index));
996
0
            debug_assert!(_was_inserted);
997
998
0
            break Some((&self.chains[chain_index], return_value));
999
        }
1000
0
    }
Unexecuted instantiation: _RNvXININtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategys0_0ppEINtB5_23UnassignSlotsAndBanIterppENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator4nextB9_
Unexecuted instantiation: _RNvXs0_NtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB5_23UnassignSlotsAndBanIterNtNtB7_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtNtB1X_4iter6traits8iterator8Iterator4nextCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvXININtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategys0_0ppEINtB5_23UnassignSlotsAndBanIterppENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator4nextB9_
Unexecuted instantiation: _RNvXs0_NtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB5_23UnassignSlotsAndBanIterNtNtB7_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator4nextCsiUjFBJteJ7x_17smoldot_full_node
1001
1002
0
    fn size_hint(&self) -> (usize, Option<usize>) {
1003
0
        self.inner_iter
1004
0
            .as_ref()
1005
0
            .map_or((0, Some(0)), |inner| inner.size_hint())
Unexecuted instantiation: _RNCNvXININtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategys0_0ppEINtB7_23UnassignSlotsAndBanIterppENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator9size_hint0Bb_
Unexecuted instantiation: _RNCNvXININtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategys0_0ppEINtB7_23UnassignSlotsAndBanIterppENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator9size_hint0Bb_
1006
0
    }
Unexecuted instantiation: _RNvXININtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategys0_0ppEINtB5_23UnassignSlotsAndBanIterppENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator9size_hintB9_
Unexecuted instantiation: _RNvXININtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategys0_0ppEINtB5_23UnassignSlotsAndBanIterppENtNtNtNtCsaYZPK01V26L_4core4iter6traits8iterator8Iterator9size_hintB9_
1007
}
1008
1009
impl<'a, TChainId, TInstant> iter::FusedIterator for UnassignSlotsAndBanIter<'a, TChainId, TInstant> where
1010
    TInstant: PartialOrd + Ord + Eq + Clone
1011
{
1012
}
1013
1014
impl<'a, TChainId, TInstant> Drop for UnassignSlotsAndBanIter<'a, TChainId, TInstant>
1015
where
1016
    TInstant: PartialOrd + Ord + Eq + Clone,
1017
{
1018
0
    fn drop(&mut self) {
1019
        // Note that this is safe because `UnassignSlotsAndBanIter` is a `FusedIterator`.
1020
0
        while let Some(_) = self.next() {}
1021
0
    }
Unexecuted instantiation: _RNvXININtNtCsN16ciHI6Qf_7smoldot7network22basic_peering_strategys2_0ppEINtB5_23UnassignSlotsAndBanIterppENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4dropB9_
Unexecuted instantiation: _RNvXs2_NtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB5_23UnassignSlotsAndBanIterNtNtB7_7service7ChainIdNtNtCsaYZPK01V26L_4core4time8DurationENtNtNtB1X_3ops4drop4Drop4dropCsDDUKWWCHAU_18smoldot_light_wasm
Unexecuted instantiation: _RNvXININtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategys2_0ppEINtB5_23UnassignSlotsAndBanIterppENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4dropB9_
Unexecuted instantiation: _RNvXs2_NtNtCseuYC0Zibziv_7smoldot7network22basic_peering_strategyINtB5_23UnassignSlotsAndBanIterNtNtB7_7service7ChainIdNtNtCsbpXXxgr6u8g_3std4time7InstantENtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4dropCsiUjFBJteJ7x_17smoldot_full_node
1022
}
1023
1024
#[cfg(test)]
1025
mod tests {
1026
    use super::{
1027
        BasicPeeringStrategy, Config, InsertAddressConnectionsResult, InsertAddressResult,
1028
        InsertChainPeerResult,
1029
    };
1030
    use crate::network::service::{peer_id::PublicKey, PeerId};
1031
    use core::time::Duration;
1032
1033
    #[test]
1034
1
    fn peer_state_ordering() {
1035
1
        // The implementation above relies on the properties tested here.
1036
1
        use super::PeerChainState;
1037
1
        assert!(PeerChainState::Assignable < PeerChainState::Banned { expires: 0 });
1038
1
        assert!(PeerChainState::Banned { expires: 5 } < PeerChainState::Banned { expires: 7 });
1039
1
        assert!(PeerChainState::Banned { expires: u32::MAX } < PeerChainState::Slot);
1040
1
    }
1041
1042
    #[test]
1043
1
    fn addresses_removed_when_peer_has_no_chain_association() {
1044
1
        let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1045
1
            randomness_seed: [0; 32],
1046
1
            peers_capacity: 0,
1047
1
            chains_capacity: 0,
1048
1
        });
1049
1
1050
1
        let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1051
1
1052
1
        assert!(
matches!0
(
1053
1
            bps.insert_chain_peer(0, peer_id.clone(), usize::MAX),
1054
            InsertChainPeerResult::Inserted { peer_removed: None }
1055
        ));
1056
1057
1
        assert!(
matches!0
(
1058
1
            bps.insert_address(&peer_id, Vec::new(), usize::MAX),
1059
            InsertAddressResult::Inserted {
1060
                address_removed: None
1061
            }
1062
        ));
1063
1064
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 1);
1065
1
        bps.unassign_slot_and_remove_chain_peer(&0, &peer_id);
1066
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 0);
1067
1
    }
1068
1069
    #[test]
1070
1
    fn addresses_not_removed_if_connected_when_peer_has_no_chain_association() {
1071
1
        let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1072
1
            randomness_seed: [0; 32],
1073
1
            peers_capacity: 0,
1074
1
            chains_capacity: 0,
1075
1
        });
1076
1
1077
1
        let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1078
1
1079
1
        assert!(
matches!0
(
1080
1
            bps.insert_chain_peer(0, peer_id.clone(), usize::MAX),
1081
            InsertChainPeerResult::Inserted { peer_removed: None }
1082
        ));
1083
1084
1
        assert!(
matches!0
(
1085
1
            bps.increase_address_connections(&peer_id, Vec::new(), usize::MAX),
1086
            InsertAddressConnectionsResult::Inserted {
1087
                address_removed: None
1088
            }
1089
        ));
1090
1091
1
        assert!(
matches!0
(
1092
1
            bps.insert_address(&peer_id, vec![1], usize::MAX),
1093
            InsertAddressResult::Inserted {
1094
                address_removed: None
1095
            }
1096
        ));
1097
1098
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 2);
1099
1
        bps.unassign_slot_and_remove_chain_peer(&0, &peer_id);
1100
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 2);
1101
1102
1
        bps.decrease_address_connections(&peer_id, &[]).unwrap();
1103
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 0);
1104
1
    }
1105
1106
    #[test]
1107
1
    fn address_not_inserted_when_peer_has_no_chain_association() {
1108
1
        let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1109
1
            randomness_seed: [0; 32],
1110
1
            peers_capacity: 0,
1111
1
            chains_capacity: 0,
1112
1
        });
1113
1
1114
1
        let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1115
1
1116
1
        assert!(
matches!0
(
1117
1
            bps.insert_address(&peer_id, Vec::new(), usize::MAX),
1118
            InsertAddressResult::UnknownPeer
1119
        ));
1120
1121
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 0);
1122
1
    }
1123
1124
    #[test]
1125
1
    fn address_connections_inserted_when_peer_has_no_chain_association() {
1126
1
        let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1127
1
            randomness_seed: [0; 32],
1128
1
            peers_capacity: 0,
1129
1
            chains_capacity: 0,
1130
1
        });
1131
1
1132
1
        let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1133
1
1134
1
        assert!(
matches!0
(
1135
1
            bps.increase_address_connections(&peer_id, Vec::new(), usize::MAX),
1136
            InsertAddressConnectionsResult::Inserted { .. }
1137
        ));
1138
1139
1
        assert_eq!(bps.peer_addresses(&peer_id).count(), 1);
1140
1
    }
1141
1142
    // TODO: more tests
1143
}