1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
//! Crate containing various traits used by moondance crates allowing to connect pallet
18
//! with each other or with mocks.
19

            
20
#![cfg_attr(not(feature = "std"), no_std)]
21

            
22
pub mod alias;
23
pub mod prod_or_fast;
24

            
25
pub use {
26
    alias::*,
27
    cumulus_primitives_core::{
28
        relay_chain::{BlockNumber, HeadData, Slot, ValidationCode},
29
        ParaId,
30
    },
31
    dp_chain_state_snapshot::{GenericStateProof, ReadEntryErr},
32
    dp_container_chain_genesis_data::ContainerChainGenesisDataItem,
33
};
34
use {
35
    core::marker::PhantomData,
36
    frame_support::{
37
        dispatch::DispatchErrorWithPostInfo,
38
        pallet_prelude::{
39
            Decode, DecodeWithMemTracking, DispatchResultWithPostInfo, Encode, Get, MaxEncodedLen,
40
            Weight,
41
        },
42
        BoundedVec,
43
    },
44
    scale_info::TypeInfo,
45
    serde::{Deserialize, Serialize},
46
    sp_core::H256,
47
    sp_runtime::{
48
        app_crypto::sp_core,
49
        traits::{CheckedAdd, CheckedMul},
50
        ArithmeticError, DispatchResult, Perbill, RuntimeDebug,
51
    },
52
    sp_std::{
53
        collections::{btree_map::BTreeMap, btree_set::BTreeSet},
54
        vec::Vec,
55
    },
56
};
57

            
58
// Separate import as rustfmt wrongly change it to `sp_std::vec::self`, which is the module instead
59
// of the macro.
60
use sp_std::vec;
61

            
62
/// The collator-assignment hook to react to collators being assigned to container chains.
63
pub trait CollatorAssignmentHook<Balance> {
64
    /// This hook is called when collators are assigned to a container
65
    ///
66
    /// The hook should never panic and is required to return the weight consumed.
67
    fn on_collators_assigned(
68
        para_id: ParaId,
69
        maybe_tip: Option<&Balance>,
70
        is_parathread: bool,
71
    ) -> Result<Weight, sp_runtime::DispatchError>;
72
}
73

            
74
#[impl_trait_for_tuples::impl_for_tuples(5)]
75
impl<Balance> CollatorAssignmentHook<Balance> for Tuple {
76
    fn on_collators_assigned(
77
        p: ParaId,
78
        t: Option<&Balance>,
79
        ip: bool,
80
    ) -> Result<Weight, sp_runtime::DispatchError> {
81
        let mut weight: Weight = Default::default();
82
        for_tuples!( #( weight.saturating_accrue(Tuple::on_collators_assigned(p, t, ip)?); )* );
83
        Ok(weight)
84
    }
85
}
86

            
87
/// Container chains collator assignment tip prioritization on congestion.
88
/// Tips paras are willing to pay for collator assignment in case of collators demand
89
/// surpasses the offer.
90
pub trait CollatorAssignmentTip<Balance> {
91
    fn get_para_tip(a: ParaId) -> Option<Balance>;
92
}
93

            
94
impl<Balance> CollatorAssignmentTip<Balance> for () {
95
    fn get_para_tip(_: ParaId) -> Option<Balance> {
96
        None
97
    }
98
}
99

            
100
pub struct AuthorNotingInfo<AccountId> {
101
    pub author: AccountId,
102
    pub block_number: BlockNumber,
103
    pub para_id: ParaId,
104
}
105

            
106
/// The author-noting hook to react to container chains authoring.
107
pub trait AuthorNotingHook<AccountId> {
108
    /// This hook is called partway through the `set_latest_author_data` inherent in author-noting.
109
    ///
110
    /// The hook should never panic and is required to return the weight consumed.
111
    fn on_container_authors_noted(info: &[AuthorNotingInfo<AccountId>]) -> Weight;
112

            
113
    #[cfg(feature = "runtime-benchmarks")]
114
    fn prepare_worst_case_for_bench(author: &AccountId, block_number: BlockNumber, para_id: ParaId);
115
}
116

            
117
#[impl_trait_for_tuples::impl_for_tuples(5)]
118
impl<AccountId> AuthorNotingHook<AccountId> for Tuple {
119
22443
    fn on_container_authors_noted(info: &[AuthorNotingInfo<AccountId>]) -> Weight {
120
22443
        let mut weight: Weight = Default::default();
121
22443
        for_tuples!( #( weight.saturating_accrue(Tuple::on_container_authors_noted(info)); )* );
122
22443
        weight
123
22443
    }
124

            
125
    #[cfg(feature = "runtime-benchmarks")]
126
    fn prepare_worst_case_for_bench(a: &AccountId, b: BlockNumber, p: ParaId) {
127
        for_tuples!( #( Tuple::prepare_worst_case_for_bench(a, b, p); )* );
128
    }
129
}
130

            
131
pub trait DistributeRewards<AccountId, Imbalance> {
132
    fn distribute_rewards(rewarded: AccountId, amount: Imbalance) -> DispatchResultWithPostInfo;
133
}
134

            
135
impl<AccountId, Imbalance> DistributeRewards<AccountId, Imbalance> for () {
136
26
    fn distribute_rewards(_rewarded: AccountId, _amount: Imbalance) -> DispatchResultWithPostInfo {
137
26
        Ok(().into())
138
26
    }
139
}
140

            
141
/// Get the current list of container chains parachain ids.
142
pub trait GetCurrentContainerChains {
143
    type MaxContainerChains: Get<u32>;
144

            
145
    fn current_container_chains() -> BoundedVec<ParaId, Self::MaxContainerChains>;
146

            
147
    #[cfg(feature = "runtime-benchmarks")]
148
    fn set_current_container_chains(container_chains: &[ParaId]);
149
}
150

            
151
#[derive(Copy, Clone, PartialEq, Eq)]
152
pub enum ForSession {
153
    Current,
154
    Next,
155
}
156

            
157
/// Get the current list of container chains parachain ids with its assigned collators.
158
/// It can return a para id with an empty list of collators.
159
pub trait GetContainerChainsWithCollators<AccountId> {
160
    fn container_chains_with_collators(for_session: ForSession) -> Vec<(ParaId, Vec<AccountId>)>;
161

            
162
    fn get_all_collators_assigned_to_chains(for_session: ForSession) -> BTreeSet<AccountId>;
163

            
164
    #[cfg(feature = "runtime-benchmarks")]
165
    fn set_container_chains_with_collators(
166
        for_session: ForSession,
167
        container_chains: &[(ParaId, Vec<AccountId>)],
168
    );
169
}
170

            
171
/// How often should a parathread collator propose blocks. The units are "1 out of n slots", where the slot time is the
172
/// tanssi slot time, 6 seconds.
173
// TODO: this is currently ignored
174
#[derive(
175
    Clone,
176
    Debug,
177
    Encode,
178
    Decode,
179
    DecodeWithMemTracking,
180
2472
    scale_info::TypeInfo,
181
    PartialEq,
182
    Eq,
183
    Serialize,
184
    Deserialize,
185
    MaxEncodedLen,
186
)]
187
pub struct SlotFrequency {
188
    /// The parathread will produce at most 1 block every x slots. min=10 means that collators can produce 1 block
189
    /// every `x >= 10` slots, but they are not enforced to. If collators produce a block after less than 10
190
    /// slots, they will not be rewarded by tanssi.
191
    pub min: u32,
192
    /// The parathread will produce at least 1 block every x slots. max=10 means that collators are forced to
193
    /// produce 1 block every `x <= 10` slots. Collators can produce a block sooner than that if the `min` allows it, but
194
    /// waiting more than 10 slots will make them lose the block reward.
195
    pub max: u32,
196
}
197

            
198
impl SlotFrequency {
199
1074
    pub fn should_parathread_buy_core(
200
1074
        &self,
201
1074
        current_slot: Slot,
202
1074
        max_slot_required_to_complete_purchase: Slot,
203
1074
        last_block_slot: Slot,
204
1074
    ) -> bool {
205
1074
        current_slot
206
1074
            >= last_block_slot
207
1074
                .saturating_add(Slot::from(u64::from(self.min)))
208
1074
                .saturating_sub(max_slot_required_to_complete_purchase)
209
1074
    }
210

            
211
    pub fn should_parathread_author_block(
212
        &self,
213
        current_slot: Slot,
214
        last_block_slot: Slot,
215
    ) -> bool {
216
        current_slot >= last_block_slot.saturating_add(Slot::from(u64::from(self.min)))
217
    }
218
}
219

            
220
impl Default for SlotFrequency {
221
675
    fn default() -> Self {
222
675
        Self { min: 1, max: 1 }
223
675
    }
224
}
225

            
226
#[derive(
227
    Clone,
228
    Debug,
229
    Encode,
230
    Decode,
231
    DecodeWithMemTracking,
232
1236
    scale_info::TypeInfo,
233
    PartialEq,
234
    Eq,
235
    Serialize,
236
    Deserialize,
237
    MaxEncodedLen,
238
)]
239
pub struct ParathreadParams {
240
    pub slot_frequency: SlotFrequency,
241
}
242

            
243
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
244
pub struct SessionContainerChains {
245
    pub parachains: Vec<ParaId>,
246
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
247
}
248

            
249
/// Get the list of container chains parachain ids at given
250
/// session index.
251
pub trait GetSessionContainerChains<SessionIndex> {
252
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
253
    #[cfg(feature = "runtime-benchmarks")]
254
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
255
}
256

            
257
/// Returns author for a parachain id for the given slot.
258
pub trait GetContainerChainAuthor<AccountId> {
259
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
260
    #[cfg(feature = "runtime-benchmarks")]
261
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
262
}
263

            
264
/// Returns the host configuration composed of the amount of collators assigned
265
/// to the orchestrator chain, and how many collators are assigned per container chain.
266
pub trait GetHostConfiguration<SessionIndex> {
267
    fn max_collators(session_index: SessionIndex) -> u32;
268
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
269
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
270
    fn collators_per_container(session_index: SessionIndex) -> u32;
271
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
272
    fn target_container_chain_fullness(session_index: SessionIndex) -> Perbill;
273
    fn max_parachain_cores_percentage(session_index: SessionIndex) -> Option<Perbill>;
274
    fn full_rotation_mode(session_index: SessionIndex) -> FullRotationModes;
275
    #[cfg(feature = "runtime-benchmarks")]
276
    fn set_host_configuration(_session_index: SessionIndex) {}
277
}
278

            
279
/// Returns current session index.
280
pub trait GetSessionIndex<SessionIndex> {
281
    fn session_index() -> SessionIndex;
282

            
283
    #[cfg(feature = "runtime-benchmarks")]
284
    fn skip_to_session(session_index: SessionIndex);
285
}
286

            
287
/// Should pallet_collator_assignment trigger a full rotation on this session?
288
pub trait ShouldRotateAllCollators<SessionIndex> {
289
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
290
}
291

            
292
impl<SessionIndex> ShouldRotateAllCollators<SessionIndex> for () {
293
    fn should_rotate_all_collators(_session_index: SessionIndex) -> bool {
294
        false
295
    }
296
}
297

            
298
/// Helper trait for pallet_collator_assignment to be able to give priority to invulnerables
299
pub trait RemoveInvulnerables<AccountId> {
300
    /// Remove the first n invulnerables from the list of collators. The order should be respected.
301
    fn remove_invulnerables(
302
        collators: &mut Vec<AccountId>,
303
        num_invulnerables: usize,
304
    ) -> Vec<AccountId>;
305
}
306

            
307
impl<AccountId: Clone> RemoveInvulnerables<AccountId> for () {
308
1177
    fn remove_invulnerables(
309
1177
        _collators: &mut Vec<AccountId>,
310
1177
        _num_invulnerables: usize,
311
1177
    ) -> Vec<AccountId> {
312
1177
        // Default impl: no collators are invulnerables
313
1177
        vec![]
314
1177
    }
315
}
316

            
317
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
318
/// in pallet_services_payment
319
pub trait ParaIdAssignmentHooks<B, AC> {
320
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
321
    /// will be the first one to get collators.
322
    fn pre_assignment(para_ids: &mut Vec<ParaId>, old_assigned: &BTreeSet<ParaId>);
323
    fn post_assignment(
324
        current_assigned: &BTreeSet<ParaId>,
325
        new_assigned: &mut BTreeMap<ParaId, Vec<AC>>,
326
        maybe_tip: &Option<B>,
327
    ) -> Weight;
328

            
329
    /// Make those para ids valid by giving them enough credits, for benchmarking.
330
    #[cfg(feature = "runtime-benchmarks")]
331
    fn make_valid_para_ids(para_ids: &[ParaId]);
332
}
333

            
334
impl<B, AC> ParaIdAssignmentHooks<B, AC> for () {
335
    fn pre_assignment(_para_ids: &mut Vec<ParaId>, _currently_assigned: &BTreeSet<ParaId>) {}
336

            
337
    fn post_assignment(
338
        _current_assigned: &BTreeSet<ParaId>,
339
        _new_assigned: &mut BTreeMap<ParaId, Vec<AC>>,
340
        _maybe_tip: &Option<B>,
341
    ) -> Weight {
342
        Default::default()
343
    }
344

            
345
    #[cfg(feature = "runtime-benchmarks")]
346
    fn make_valid_para_ids(_para_ids: &[ParaId]) {}
347
}
348

            
349
pub trait RelayStorageRootProvider {
350
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
351

            
352
    #[cfg(feature = "runtime-benchmarks")]
353
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
354
}
355

            
356
impl RelayStorageRootProvider for () {
357
    fn get_relay_storage_root(_relay_block_number: u32) -> Option<H256> {
358
        None
359
    }
360

            
361
    #[cfg(feature = "runtime-benchmarks")]
362
    fn set_relay_storage_root(_relay_block_number: u32, _storage_root: Option<H256>) {}
363
}
364

            
365
/// Information extracted from the latest container chain header
366
#[derive(
367
    Default,
368
    Clone,
369
    Encode,
370
    Decode,
371
    PartialEq,
372
    sp_core::RuntimeDebug,
373
1854
    scale_info::TypeInfo,
374
    MaxEncodedLen,
375
    Serialize,
376
    Deserialize,
377
)]
378
pub struct ContainerChainBlockInfo<AccountId> {
379
    pub block_number: BlockNumber,
380
    pub author: AccountId,
381
    pub latest_slot_number: Slot,
382
}
383

            
384
pub trait LatestAuthorInfoFetcher<AccountId> {
385
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
386
}
387

            
388
pub trait StorageDeposit<Data, Balance> {
389
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
390
}
391

            
392
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
393
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
394
    for BytesDeposit<BaseCost, ByteCost>
395
where
396
    Data: Encode,
397
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
398
    BaseCost: Get<Balance>,
399
    ByteCost: Get<Balance>,
400
{
401
194
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
402
194
        let base = BaseCost::get();
403
194
        let byte = ByteCost::get();
404
194
        let size: Balance = data
405
194
            .encoded_size()
406
194
            .try_into()
407
194
            .map_err(|_| ArithmeticError::Overflow)?;
408

            
409
194
        let deposit = byte
410
194
            .checked_mul(&size)
411
194
            .ok_or(ArithmeticError::Overflow)?
412
194
            .checked_add(&base)
413
194
            .ok_or(ArithmeticError::Overflow)?;
414

            
415
194
        Ok(deposit)
416
194
    }
417
}
418

            
419
/// Trait to abstract away relay storage proofs, and allow the same logic to work on both parachains and solochains.
420
/// Parachains should use relay storage proofs, while solochains should read from storage directly.
421
pub trait GenericStorageReader {
422
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr>;
423
}
424

            
425
impl GenericStorageReader for GenericStateProof<cumulus_primitives_core::relay_chain::Block> {
426
23482
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
427
23482
        GenericStateProof::read_entry(self, key, fallback)
428
23482
    }
429
}
430

            
431
/// Solo chain impl, read directly from storage
432
pub struct NativeStorageReader;
433
impl GenericStorageReader for NativeStorageReader {
434
22
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
435
22
        match frame_support::storage::unhashed::get(key).or(fallback) {
436
22
            Some(x) => Ok(x),
437
            None => Err(ReadEntryErr::Absent),
438
        }
439
22
    }
440
}
441

            
442
/// Trait to handle registrar-related operations in a relay-chain context.
443
/// Mostly used to wire Tanssi's and Polkadot's registrars, for them to
444
/// work together in a solo-chain environment.
445
pub trait RegistrarHandler<AccountId> {
446
    fn register(
447
        who: AccountId,
448
        id: ParaId,
449
        genesis_storage: &[ContainerChainGenesisDataItem],
450
        head_data: Option<HeadData>,
451
    ) -> DispatchResult;
452

            
453
    fn schedule_para_upgrade(id: ParaId) -> DispatchResult;
454
    fn schedule_para_downgrade(id: ParaId) -> DispatchResult;
455
    fn deregister(id: ParaId);
456
    fn deregister_weight() -> Weight;
457

            
458
    #[cfg(feature = "runtime-benchmarks")]
459
    fn bench_head_data() -> Option<HeadData> {
460
        None
461
    }
462
    #[cfg(feature = "runtime-benchmarks")]
463
    fn add_trusted_validation_code(_code: Vec<u8>) {}
464
    #[cfg(feature = "runtime-benchmarks")]
465
    fn registrar_new_session(_session: u32) {}
466
    #[cfg(feature = "runtime-benchmarks")]
467
    fn prepare_chain_registration(_id: ParaId, _who: AccountId) {}
468
}
469

            
470
impl<AccountId> RegistrarHandler<AccountId> for () {
471
151
    fn register(
472
151
        _who: AccountId,
473
151
        _id: ParaId,
474
151
        _genesis_storage: &[ContainerChainGenesisDataItem],
475
151
        _head_data: Option<HeadData>,
476
151
    ) -> DispatchResult {
477
151
        Ok(())
478
151
    }
479

            
480
64
    fn schedule_para_upgrade(_id: ParaId) -> DispatchResult {
481
64
        Ok(())
482
64
    }
483

            
484
70
    fn schedule_para_downgrade(_id: ParaId) -> DispatchResult {
485
70
        Ok(())
486
70
    }
487

            
488
42
    fn deregister(_id: ParaId) {}
489

            
490
42
    fn deregister_weight() -> Weight {
491
42
        Weight::default()
492
42
    }
493
}
494

            
495
/// Trait to retrieve the orchestrator block author (if any).
496
/// In a relay-chain context we will return None.
497
pub trait MaybeSelfChainBlockAuthor<AccountId> {
498
    fn get_block_author() -> Option<AccountId>;
499
}
500

            
501
impl<AccountId> MaybeSelfChainBlockAuthor<AccountId> for () {
502
14132
    fn get_block_author() -> Option<AccountId> {
503
14132
        None
504
14132
    }
505
}
506

            
507
/// Information regarding the active era (era in used in session).
508
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
509
pub struct ActiveEraInfo {
510
    /// Index of era.
511
    pub index: EraIndex,
512
    /// Moment of start expressed as millisecond from `$UNIX_EPOCH`.
513
    ///
514
    /// Start can be none if start hasn't been set for the era yet,
515
    /// Start is set on the first on_finalize of the era to guarantee usage of `Time`.
516
    pub start: Option<u64>,
517
}
518

            
519
/// Counter for the number of eras that have passed.
520
pub type EraIndex = u32;
521

            
522
pub trait EraIndexProvider {
523
    fn active_era() -> ActiveEraInfo;
524
    fn era_to_session_start(era_index: EraIndex) -> Option<u32>;
525
}
526

            
527
pub trait ValidatorProvider<ValidatorId> {
528
    fn validators() -> Vec<ValidatorId>;
529
}
530

            
531
pub trait InvulnerablesProvider<ValidatorId> {
532
    fn invulnerables() -> Vec<ValidatorId>;
533
}
534

            
535
pub trait OnEraStart {
536
    fn on_era_start(_era_index: EraIndex, _session_start: u32, _external_idx: u64) {}
537
}
538

            
539
#[impl_trait_for_tuples::impl_for_tuples(5)]
540
impl OnEraStart for Tuple {
541
665
    fn on_era_start(era_index: EraIndex, session_start: u32, external_idx: u64) {
542
665
        for_tuples!( #( Tuple::on_era_start(era_index, session_start, external_idx); )* );
543
665
    }
544
}
545

            
546
pub trait OnEraEnd {
547
    fn on_era_end(_era_index: EraIndex) {}
548
}
549

            
550
#[impl_trait_for_tuples::impl_for_tuples(5)]
551
impl OnEraEnd for Tuple {
552
    fn on_era_end(era_index: EraIndex) {
553
        for_tuples!( #( Tuple::on_era_end(era_index); )* );
554
    }
555
}
556

            
557
/// Strategy to use when rotating collators. Default: rotate all of them. Allows to rotate only a random subset.
558
#[derive(
559
    Clone,
560
    Debug,
561
    Default,
562
    Encode,
563
    Decode,
564
    DecodeWithMemTracking,
565
4944
    scale_info::TypeInfo,
566
    PartialEq,
567
    Eq,
568
    Serialize,
569
    Deserialize,
570
    MaxEncodedLen,
571
)]
572
pub enum FullRotationMode {
573
140389
    #[default]
574
    RotateAll,
575
729
    KeepAll,
576
    /// Keep this many collators
577
    KeepCollators {
578
        keep: u32,
579
    },
580
    /// Keep a ratio of collators wrt to max collators.
581
    /// If max collators changes, the number of collators kept also changes.
582
    KeepPerbill {
583
        percentage: Perbill,
584
    },
585
}
586

            
587
/// Allow to set a different [FullRotationMode] for each kind of chain. Default: rotate all.
588
#[derive(
589
    Clone,
590
    Debug,
591
    Default,
592
    Encode,
593
    DecodeWithMemTracking,
594
    Decode,
595
3708
    scale_info::TypeInfo,
596
    PartialEq,
597
    Eq,
598
    Serialize,
599
    Deserialize,
600
    MaxEncodedLen,
601
)]
602
pub struct FullRotationModes {
603
    pub orchestrator: FullRotationMode,
604
    pub parachain: FullRotationMode,
605
    pub parathread: FullRotationMode,
606
}
607

            
608
impl FullRotationModes {
609
    /// Keep all collators assigned to their current chain if possible. This is equivalent to disabling rotation.
610
49440
    pub fn keep_all() -> Self {
611
49440
        Self {
612
49440
            orchestrator: FullRotationMode::KeepAll,
613
49440
            parachain: FullRotationMode::KeepAll,
614
49440
            parathread: FullRotationMode::KeepAll,
615
49440
        }
616
49440
    }
617
}
618

            
619
// A trait to retrieve the external index provider identifying some set of data
620
// In starlight, used to retrieve the external index associated to validators
621
pub trait ExternalIndexProvider {
622
    fn get_external_index() -> u64;
623
}
624

            
625
// A trait to verify if a node has been inactive during the last minimum activity
626
pub trait NodeActivityTrackingHelper<AccountId> {
627
    fn is_node_inactive(node: &AccountId) -> bool;
628
}
629

            
630
// A trait to help verify if a ParaId is a chain or parathread
631
pub trait ParathreadHelper {
632
    fn get_parathreads_for_session() -> BTreeSet<ParaId>;
633
}