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
extern crate alloc;
23

            
24
pub mod alias;
25
pub mod prod_or_fast;
26

            
27
pub use {
28
    alias::*,
29
    cumulus_primitives_core::{
30
        relay_chain::{BlockNumber, HeadData, Slot, ValidationCode},
31
        ParaId,
32
    },
33
    dp_chain_state_snapshot::{GenericStateProof, ReadEntryErr},
34
    dp_container_chain_genesis_data::ContainerChainGenesisDataItem,
35
};
36

            
37
use {
38
    alloc::{
39
        collections::{btree_map::BTreeMap, btree_set::BTreeSet},
40
        vec,
41
        vec::Vec,
42
    },
43
    core::marker::PhantomData,
44
    frame_support::{
45
        dispatch::DispatchErrorWithPostInfo,
46
        pallet_prelude::{
47
            Decode, DecodeWithMemTracking, DispatchResultWithPostInfo, Encode, Get, MaxEncodedLen,
48
            Weight,
49
        },
50
    },
51
    scale_info::TypeInfo,
52
    serde::{Deserialize, Serialize},
53
    sp_core::H256,
54
    sp_runtime::{
55
        app_crypto::sp_core,
56
        traits::{CheckedAdd, CheckedMul},
57
        ArithmeticError, DispatchResult, Perbill, RuntimeDebug,
58
    },
59
};
60

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

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

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

            
94
impl<Balance> CollatorAssignmentTip<Balance> for () {
95
    fn get_para_max_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
    /// Some benchmarks need a special setup: register accounts, transfer tokens, etc.
115
    /// Use this method to perform that setup. In some cases, there is a mandatory delay needed to
116
    /// execute something, for example in `pallet_pooled_staking`. In that case, use
117
    /// `bench_advance_block` to fast-forward the chain until the wait period is over, and
118
    /// `bench_execute_pending` to execute any pending operations.
119
    fn prepare_worst_case_for_bench(author: &AccountId, block_number: BlockNumber, para_id: ParaId);
120
    #[cfg(feature = "runtime-benchmarks")]
121
    /// Advances the chain until all the conditions required by `bench_execute_pending` have been
122
    /// met. Assumes that the initial setup was done in block 0. So this does nothing if the chain
123
    /// has already been advanced before.
124
    fn bench_advance_block() {}
125
    #[cfg(feature = "runtime-benchmarks")]
126
    /// Executes any pending steps from `prepare_worst_case_for_bench`.
127
    /// Assumes that `bench_advance_block` has already been called.
128
    fn bench_execute_pending() {}
129
}
130

            
131
#[impl_trait_for_tuples::impl_for_tuples(5)]
132
impl<AccountId> AuthorNotingHook<AccountId> for Tuple {
133
142
    fn on_container_authors_noted(info: &[AuthorNotingInfo<AccountId>]) -> Weight {
134
142
        let mut weight: Weight = Default::default();
135
131
        for_tuples!( #( weight.saturating_accrue(Tuple::on_container_authors_noted(info)); )* );
136
142
        weight
137
142
    }
138

            
139
    #[cfg(feature = "runtime-benchmarks")]
140
    fn prepare_worst_case_for_bench(a: &AccountId, b: BlockNumber, p: ParaId) {
141
        for_tuples!( #( Tuple::prepare_worst_case_for_bench(a, b, p); )* );
142
    }
143

            
144
    #[cfg(feature = "runtime-benchmarks")]
145
    fn bench_advance_block() {
146
        for_tuples!( #( Tuple::bench_advance_block(); )* );
147
    }
148

            
149
    #[cfg(feature = "runtime-benchmarks")]
150
    fn bench_execute_pending() {
151
        for_tuples!( #( Tuple::bench_execute_pending(); )* );
152
    }
153
}
154

            
155
pub trait DistributeRewards<AccountId, Imbalance> {
156
    fn distribute_rewards(rewarded: AccountId, amount: Imbalance) -> DispatchResultWithPostInfo;
157
    #[cfg(feature = "runtime-benchmarks")]
158
    /// Some benchmarks need a special setup: register accounts, transfer tokens, etc.
159
    /// Use this method to perform that setup. In some cases, there is a mandatory delay needed to
160
    /// execute something, for example in `pallet_pooled_staking`. In that case, use
161
    /// `bench_advance_block` to fast-forward the chain until the wait period is over, and
162
    /// `bench_execute_pending` to execute any pending operations.
163
    fn prepare_worst_case_for_bench(a: &AccountId);
164
    #[cfg(feature = "runtime-benchmarks")]
165
    /// Advances the chain until all the conditions required by `bench_execute_pending` have been
166
    /// met. Assumes that the initial setup was done in block 0. So this does nothing if the chain
167
    /// has already been advanced before.
168
    fn bench_advance_block() {}
169
    #[cfg(feature = "runtime-benchmarks")]
170
    /// Executes any pending steps from `prepare_worst_case_for_bench`.
171
    /// Assumes that `bench_advance_block` has already been called.
172
    fn bench_execute_pending() {}
173
}
174

            
175
impl<AccountId, Imbalance> DistributeRewards<AccountId, Imbalance> for () {
176
26
    fn distribute_rewards(_rewarded: AccountId, _amount: Imbalance) -> DispatchResultWithPostInfo {
177
26
        Ok(().into())
178
26
    }
179
    #[cfg(feature = "runtime-benchmarks")]
180
    fn prepare_worst_case_for_bench(_a: &AccountId) {}
181
}
182

            
183
#[derive(Copy, Clone, PartialEq, Eq)]
184
pub enum ForSession {
185
    Current,
186
    Next,
187
}
188

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

            
194
    fn get_all_collators_assigned_to_chains(for_session: ForSession) -> BTreeSet<AccountId>;
195

            
196
    #[cfg(feature = "runtime-benchmarks")]
197
    fn set_container_chains_with_collators(
198
        for_session: ForSession,
199
        container_chains: &[(ParaId, Vec<AccountId>)],
200
    );
201
}
202

            
203
/// How often should a parathread collator propose blocks. The units are "1 out of n slots", where the slot time is the
204
/// tanssi slot time, 6 seconds.
205
// TODO: this is currently ignored
206
#[derive(
207
    Clone,
208
    Debug,
209
    Encode,
210
    Decode,
211
    DecodeWithMemTracking,
212
    scale_info::TypeInfo,
213
    PartialEq,
214
    Eq,
215
    Serialize,
216
    Deserialize,
217
    MaxEncodedLen,
218
)]
219
pub struct SlotFrequency {
220
    /// The parathread will produce at most 1 block every x slots. min=10 means that collators can produce 1 block
221
    /// every `x >= 10` slots, but they are not enforced to. If collators produce a block after less than 10
222
    /// slots, they will not be rewarded by tanssi.
223
    pub min: u32,
224
    /// The parathread will produce at least 1 block every x slots. max=10 means that collators are forced to
225
    /// produce 1 block every `x <= 10` slots. Collators can produce a block sooner than that if the `min` allows it, but
226
    /// waiting more than 10 slots will make them lose the block reward.
227
    pub max: u32,
228
}
229

            
230
impl SlotFrequency {
231
1155
    pub fn should_parathread_buy_core(
232
1155
        &self,
233
1155
        current_slot: Slot,
234
1155
        max_slot_required_to_complete_purchase: Slot,
235
1155
        last_block_slot: Slot,
236
1155
    ) -> bool {
237
1155
        current_slot
238
1155
            >= last_block_slot
239
1155
                .saturating_add(Slot::from(u64::from(self.min)))
240
1155
                .saturating_sub(max_slot_required_to_complete_purchase)
241
1155
    }
242

            
243
    pub fn should_parathread_author_block(
244
        &self,
245
        current_slot: Slot,
246
        last_block_slot: Slot,
247
    ) -> bool {
248
        current_slot >= last_block_slot.saturating_add(Slot::from(u64::from(self.min)))
249
    }
250
}
251

            
252
impl Default for SlotFrequency {
253
605
    fn default() -> Self {
254
605
        Self { min: 1, max: 1 }
255
605
    }
256
}
257

            
258
#[derive(
259
    Clone,
260
    Debug,
261
    Encode,
262
    Decode,
263
    DecodeWithMemTracking,
264
    scale_info::TypeInfo,
265
    PartialEq,
266
    Eq,
267
    Serialize,
268
    Deserialize,
269
    MaxEncodedLen,
270
)]
271
pub struct ParathreadParams {
272
    pub slot_frequency: SlotFrequency,
273
}
274

            
275
#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)]
276
pub struct SessionContainerChains {
277
    pub parachains: Vec<ParaId>,
278
    pub parathreads: Vec<(ParaId, ParathreadParams)>,
279
}
280

            
281
/// Get the list of container chains parachain ids at given
282
/// session index.
283
pub trait GetSessionContainerChains<SessionIndex> {
284
    fn session_container_chains(session_index: SessionIndex) -> SessionContainerChains;
285
    #[cfg(feature = "runtime-benchmarks")]
286
    fn set_session_container_chains(session_index: SessionIndex, container_chains: &[ParaId]);
287
}
288

            
289
/// Returns author for a parachain id for the given slot.
290
pub trait GetContainerChainAuthor<AccountId> {
291
    fn author_for_slot(slot: Slot, para_id: ParaId) -> Option<AccountId>;
292
    #[cfg(feature = "runtime-benchmarks")]
293
    fn set_authors_for_para_id(para_id: ParaId, authors: Vec<AccountId>);
294
}
295

            
296
/// Returns the host configuration composed of the amount of collators assigned
297
/// to the orchestrator chain, and how many collators are assigned per container chain.
298
pub trait GetHostConfiguration<SessionIndex> {
299
    fn max_collators(session_index: SessionIndex) -> u32;
300
    fn min_collators_for_orchestrator(session_index: SessionIndex) -> u32;
301
    fn max_collators_for_orchestrator(session_index: SessionIndex) -> u32;
302
    fn collators_per_container(session_index: SessionIndex) -> u32;
303
    fn collators_per_parathread(session_index: SessionIndex) -> u32;
304
    fn target_container_chain_fullness(session_index: SessionIndex) -> Perbill;
305
    fn max_parachain_cores_percentage(session_index: SessionIndex) -> Option<Perbill>;
306
    fn full_rotation_mode(session_index: SessionIndex) -> FullRotationModes;
307
    #[cfg(feature = "runtime-benchmarks")]
308
    fn set_host_configuration(_session_index: SessionIndex) {}
309
}
310

            
311
/// Returns current session index.
312
pub trait GetSessionIndex<SessionIndex> {
313
    fn session_index() -> SessionIndex;
314

            
315
    #[cfg(feature = "runtime-benchmarks")]
316
    fn skip_to_session(session_index: SessionIndex);
317
}
318

            
319
/// Should pallet_collator_assignment trigger a full rotation on this session?
320
pub trait ShouldRotateAllCollators<SessionIndex> {
321
    fn should_rotate_all_collators(session_index: SessionIndex) -> bool;
322
}
323

            
324
impl<SessionIndex> ShouldRotateAllCollators<SessionIndex> for () {
325
    fn should_rotate_all_collators(_session_index: SessionIndex) -> bool {
326
        false
327
    }
328
}
329

            
330
/// Helper trait for pallet_collator_assignment to be able to give priority to invulnerables
331
pub trait RemoveInvulnerables<AccountId> {
332
    /// Remove the first n invulnerables from the list of collators. The order should be respected.
333
    fn remove_invulnerables(
334
        collators: &mut Vec<AccountId>,
335
        num_invulnerables: usize,
336
    ) -> Vec<AccountId>;
337
}
338

            
339
impl<AccountId: Clone> RemoveInvulnerables<AccountId> for () {
340
2172
    fn remove_invulnerables(
341
2172
        _collators: &mut Vec<AccountId>,
342
2172
        _num_invulnerables: usize,
343
2172
    ) -> Vec<AccountId> {
344
        // Default impl: no collators are invulnerables
345
2172
        vec![]
346
2172
    }
347
}
348

            
349
/// Helper trait for pallet_collator_assignment to be able to not assign collators to container chains with no credits
350
/// in pallet_services_payment
351
pub trait ParaIdAssignmentHooks<B, AC> {
352
    /// Remove para ids with not enough credits. The resulting order will affect priority: the first para id in the list
353
    /// will be the first one to get collators.
354
    fn pre_assignment(para_ids: &mut Vec<ParaId>, old_assigned: &BTreeSet<ParaId>);
355
    fn post_assignment(
356
        current_assigned: &BTreeSet<ParaId>,
357
        new_assigned: &mut BTreeMap<ParaId, Vec<AC>>,
358
        maybe_tip: &Option<B>,
359
    ) -> Weight;
360

            
361
    /// Make those para ids valid by giving them enough credits, for benchmarking.
362
    #[cfg(feature = "runtime-benchmarks")]
363
    fn make_valid_para_ids(para_ids: &[ParaId]);
364
}
365

            
366
impl<B, AC> ParaIdAssignmentHooks<B, AC> for () {
367
    fn pre_assignment(_para_ids: &mut Vec<ParaId>, _currently_assigned: &BTreeSet<ParaId>) {}
368

            
369
    fn post_assignment(
370
        _current_assigned: &BTreeSet<ParaId>,
371
        _new_assigned: &mut BTreeMap<ParaId, Vec<AC>>,
372
        _maybe_tip: &Option<B>,
373
    ) -> Weight {
374
        Default::default()
375
    }
376

            
377
    #[cfg(feature = "runtime-benchmarks")]
378
    fn make_valid_para_ids(_para_ids: &[ParaId]) {}
379
}
380

            
381
pub trait RelayStorageRootProvider {
382
    fn get_relay_storage_root(relay_block_number: u32) -> Option<H256>;
383

            
384
    #[cfg(feature = "runtime-benchmarks")]
385
    fn set_relay_storage_root(relay_block_number: u32, storage_root: Option<H256>);
386
}
387

            
388
impl RelayStorageRootProvider for () {
389
    fn get_relay_storage_root(_relay_block_number: u32) -> Option<H256> {
390
        None
391
    }
392

            
393
    #[cfg(feature = "runtime-benchmarks")]
394
    fn set_relay_storage_root(_relay_block_number: u32, _storage_root: Option<H256>) {}
395
}
396

            
397
/// Information extracted from the latest container chain header
398
#[derive(
399
    Default,
400
    Clone,
401
    Encode,
402
    Decode,
403
    PartialEq,
404
    sp_core::RuntimeDebug,
405
    scale_info::TypeInfo,
406
    MaxEncodedLen,
407
    Serialize,
408
    Deserialize,
409
)]
410
pub struct ContainerChainBlockInfo<AccountId> {
411
    pub block_number: BlockNumber,
412
    pub author: AccountId,
413
    pub latest_slot_number: Slot,
414
}
415

            
416
pub trait LatestAuthorInfoFetcher<AccountId> {
417
    fn get_latest_author_info(para_id: ParaId) -> Option<ContainerChainBlockInfo<AccountId>>;
418
}
419

            
420
pub trait StorageDeposit<Data, Balance> {
421
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo>;
422
}
423

            
424
pub struct BytesDeposit<BaseCost, ByteCost>(PhantomData<(BaseCost, ByteCost)>);
425
impl<Data, Balance, BaseCost, ByteCost> StorageDeposit<Data, Balance>
426
    for BytesDeposit<BaseCost, ByteCost>
427
where
428
    Data: Encode,
429
    Balance: TryFrom<usize> + CheckedAdd + CheckedMul,
430
    BaseCost: Get<Balance>,
431
    ByteCost: Get<Balance>,
432
{
433
103
    fn compute_deposit(data: &Data) -> Result<Balance, DispatchErrorWithPostInfo> {
434
103
        let base = BaseCost::get();
435
103
        let byte = ByteCost::get();
436
103
        let size: Balance = data
437
103
            .encoded_size()
438
103
            .try_into()
439
103
            .map_err(|_| ArithmeticError::Overflow)?;
440

            
441
103
        let deposit = byte
442
103
            .checked_mul(&size)
443
103
            .ok_or(ArithmeticError::Overflow)?
444
103
            .checked_add(&base)
445
103
            .ok_or(ArithmeticError::Overflow)?;
446

            
447
103
        Ok(deposit)
448
103
    }
449
}
450

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

            
457
impl GenericStorageReader for GenericStateProof<cumulus_primitives_core::relay_chain::Block> {
458
142
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
459
142
        GenericStateProof::read_entry(self, key, fallback)
460
142
    }
461
}
462

            
463
/// Solo chain impl, read directly from storage
464
pub struct NativeStorageReader;
465
impl GenericStorageReader for NativeStorageReader {
466
36
    fn read_entry<T: Decode>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr> {
467
36
        match frame_support::storage::unhashed::get(key).or(fallback) {
468
36
            Some(x) => Ok(x),
469
            None => Err(ReadEntryErr::Absent),
470
        }
471
36
    }
472
}
473

            
474
/// Trait to handle registrar-related operations in a relay-chain context.
475
/// Mostly used to wire Tanssi's and Polkadot's registrars, for them to
476
/// work together in a solo-chain environment.
477
pub trait RegistrarHandler<AccountId> {
478
    fn register(
479
        who: AccountId,
480
        id: ParaId,
481
        genesis_storage: &[ContainerChainGenesisDataItem],
482
        head_data: Option<HeadData>,
483
    ) -> DispatchResult;
484

            
485
    fn schedule_para_upgrade(id: ParaId) -> DispatchResult;
486
    fn schedule_para_downgrade(id: ParaId) -> DispatchResult;
487
    fn deregister(id: ParaId);
488
    fn deregister_weight() -> Weight;
489

            
490
    #[cfg(feature = "runtime-benchmarks")]
491
    fn bench_head_data() -> Option<HeadData> {
492
        None
493
    }
494
    #[cfg(feature = "runtime-benchmarks")]
495
    fn add_trusted_validation_code(_code: Vec<u8>) {}
496
    #[cfg(feature = "runtime-benchmarks")]
497
    fn registrar_new_session(_session: u32) {}
498
    #[cfg(feature = "runtime-benchmarks")]
499
    fn prepare_chain_registration(_id: ParaId, _who: AccountId) {}
500
}
501

            
502
impl<AccountId> RegistrarHandler<AccountId> for () {
503
63
    fn register(
504
63
        _who: AccountId,
505
63
        _id: ParaId,
506
63
        _genesis_storage: &[ContainerChainGenesisDataItem],
507
63
        _head_data: Option<HeadData>,
508
63
    ) -> DispatchResult {
509
63
        Ok(())
510
63
    }
511

            
512
40
    fn schedule_para_upgrade(_id: ParaId) -> DispatchResult {
513
40
        Ok(())
514
40
    }
515

            
516
10
    fn schedule_para_downgrade(_id: ParaId) -> DispatchResult {
517
10
        Ok(())
518
10
    }
519

            
520
    fn deregister(_id: ParaId) {}
521

            
522
    fn deregister_weight() -> Weight {
523
        Weight::default()
524
    }
525
}
526

            
527
/// Trait to retrieve the orchestrator block author (if any).
528
/// In a relay-chain context we will return None.
529
pub trait MaybeSelfChainBlockAuthor<AccountId> {
530
    fn get_block_author() -> Option<AccountId>;
531
}
532

            
533
impl<AccountId> MaybeSelfChainBlockAuthor<AccountId> for () {
534
19087
    fn get_block_author() -> Option<AccountId> {
535
19087
        None
536
19087
    }
537
}
538

            
539
/// Information regarding the active era (era in used in session).
540
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
541
pub struct ActiveEraInfo {
542
    /// Index of era.
543
    pub index: EraIndex,
544
    /// Moment of start expressed as millisecond from `$UNIX_EPOCH`.
545
    ///
546
    /// Start can be none if start hasn't been set for the era yet,
547
    /// Start is set on the first on_finalize of the era to guarantee usage of `Time`.
548
    pub start: Option<u64>,
549
}
550

            
551
/// Counter for the number of eras that have passed.
552
pub type EraIndex = u32;
553

            
554
pub trait EraIndexProvider {
555
    fn active_era() -> ActiveEraInfo;
556
    fn era_to_session_start(era_index: EraIndex) -> Option<u32>;
557
}
558

            
559
pub trait ValidatorProvider<ValidatorId> {
560
    fn validators() -> Vec<ValidatorId>;
561
}
562

            
563
pub trait InvulnerablesProvider<ValidatorId> {
564
    fn invulnerables() -> Vec<ValidatorId>;
565
}
566

            
567
pub trait OnEraStart {
568
    fn on_era_start(_era_index: EraIndex, _session_start: u32, _external_idx: u64) {}
569
}
570

            
571
#[impl_trait_for_tuples::impl_for_tuples(5)]
572
impl OnEraStart for Tuple {
573
1441
    fn on_era_start(era_index: EraIndex, session_start: u32, external_idx: u64) {
574
1441
        for_tuples!( #( Tuple::on_era_start(era_index, session_start, external_idx); )* );
575
1441
    }
576
}
577

            
578
pub trait OnEraEnd {
579
    fn on_era_end(_era_index: EraIndex) {}
580
}
581

            
582
#[impl_trait_for_tuples::impl_for_tuples(5)]
583
impl OnEraEnd for Tuple {
584
    fn on_era_end(era_index: EraIndex) {
585
        for_tuples!( #( Tuple::on_era_end(era_index); )* );
586
    }
587
}
588

            
589
/// Strategy to use when rotating collators. Default: rotate all of them. Allows to rotate only a random subset.
590
#[derive(
591
    Clone,
592
    Debug,
593
    Default,
594
    Encode,
595
    Decode,
596
    DecodeWithMemTracking,
597
    scale_info::TypeInfo,
598
    PartialEq,
599
    Eq,
600
    Serialize,
601
    Deserialize,
602
    MaxEncodedLen,
603
)]
604
pub enum FullRotationMode {
605
    #[default]
606
    RotateAll,
607
    KeepAll,
608
    /// Keep this many collators
609
    KeepCollators {
610
        keep: u32,
611
    },
612
    /// Keep a ratio of collators wrt to max collators.
613
    /// If max collators changes, the number of collators kept also changes.
614
    KeepPerbill {
615
        percentage: Perbill,
616
    },
617
}
618

            
619
/// Allow to set a different [FullRotationMode] for each kind of chain. Default: rotate all.
620
#[derive(
621
    Clone,
622
    Debug,
623
    Default,
624
    Encode,
625
    DecodeWithMemTracking,
626
    Decode,
627
    scale_info::TypeInfo,
628
    PartialEq,
629
    Eq,
630
    Serialize,
631
    Deserialize,
632
    MaxEncodedLen,
633
)]
634
pub struct FullRotationModes {
635
    pub orchestrator: FullRotationMode,
636
    pub parachain: FullRotationMode,
637
    pub parathread: FullRotationMode,
638
}
639

            
640
impl FullRotationModes {
641
    /// Keep all collators assigned to their current chain if possible. This is equivalent to disabling rotation.
642
66671
    pub fn keep_all() -> Self {
643
66671
        Self {
644
66671
            orchestrator: FullRotationMode::KeepAll,
645
66671
            parachain: FullRotationMode::KeepAll,
646
66671
            parathread: FullRotationMode::KeepAll,
647
66671
        }
648
66671
    }
649
}
650

            
651
// A trait to retrieve the external index provider identifying some set of data
652
// In starlight, used to retrieve the external index associated to validators
653
pub trait ExternalIndexProvider {
654
    fn get_external_index() -> u64;
655
}
656

            
657
// A trait to check invulnerables
658
pub trait InvulnerablesHelper<AccountId> {
659
    /// Checks if the given `AccountId` is invulnerable.
660
    fn is_invulnerable(account_id: &AccountId) -> bool;
661
}
662

            
663
// A trait to verify the inactivity status of nodes
664
// and handle the offline status of nodes
665
pub trait NodeActivityTrackingHelper<AccountId> {
666
    /// Check if a node is inactive.
667
    fn is_node_inactive(node: &AccountId) -> bool;
668
    /// Check if a node is offline.
669
    fn is_node_offline(node: &AccountId) -> bool;
670
    #[cfg(feature = "runtime-benchmarks")]
671
    /// Marks online node as online
672
    fn make_node_online(node: &AccountId);
673
    /// Marks node as inactive for the current activity window so it could be notified as inactive
674
    #[cfg(feature = "runtime-benchmarks")]
675
    fn make_node_inactive(node: &AccountId);
676
}
677

            
678
// A trait to help verify if a ParaId is a chain or parathread
679
pub trait ParathreadHelper {
680
    fn get_parathreads_for_session() -> BTreeSet<ParaId>;
681
}
682

            
683
// A trait to help updating the collators rewards when a collator's online status changes.
684
pub trait StakingCandidateHelper<AccountId> {
685
    /// Check if the candidate is in SortedEligibleCandidates list.
686
    fn is_candidate_selected(candidate: &AccountId) -> bool;
687
    /// Updates stake when candidate's online status change.
688
    fn on_online_status_change(
689
        candidate: &AccountId,
690
        is_online: bool,
691
    ) -> DispatchResultWithPostInfo;
692
    /// Benchmarking helper function that makes collator part of the SortedEligibleCollators list.
693
    #[cfg(feature = "runtime-benchmarks")]
694
    fn make_collator_eligible_candidate(collator: &AccountId);
695
}