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
//! ExternalValidators pallet.
18
//!
19
//! A pallet to manage external validators for a solochain.
20
//!
21
//! ## Terminology
22
//!
23
//! - WhitelistedValidators: Fixed validators set by root/governance. Have priority over the external validators.
24
//! - ExternalValidators: Validators set using storage proofs from another blockchain. Can be disabled by setting
25
//!     `SkipExternalValidators` to true.
26
//!
27
//! Validators only change once per era. By default the era changes after a fixed number of sessions, but new eras
28
//! can be forced or disabled using a root extrinsic.
29
//!
30
//! The structure of this pallet and the concept of eras is inspired by `pallet_staking` from Polkadot.
31

            
32
#![cfg_attr(not(feature = "std"), no_std)]
33

            
34
pub use pallet::*;
35
use {
36
    frame_support::pallet_prelude::Weight,
37
    log::log,
38
    parity_scale_codec::{Decode, Encode, MaxEncodedLen},
39
    scale_info::TypeInfo,
40
    sp_runtime::traits::Get,
41
    sp_runtime::RuntimeDebug,
42
    sp_staking::SessionIndex,
43
    sp_std::collections::btree_set::BTreeSet,
44
    sp_std::vec::Vec,
45
    tp_traits::{
46
        ActiveEraInfo, EraIndex, EraIndexProvider, InvulnerablesProvider, OnEraEnd, OnEraStart,
47
        ValidatorProvider,
48
    },
49
};
50

            
51
#[cfg(test)]
52
mod mock;
53

            
54
#[cfg(test)]
55
mod tests;
56

            
57
#[cfg(feature = "runtime-benchmarks")]
58
mod benchmarking;
59
pub mod weights;
60

            
61
48
#[frame_support::pallet]
62
pub mod pallet {
63
    pub use crate::weights::WeightInfo;
64

            
65
    #[cfg(feature = "runtime-benchmarks")]
66
    use frame_support::traits::Currency;
67

            
68
    use {
69
        super::*,
70
        frame_support::{
71
            dispatch::DispatchResultWithPostInfo,
72
            pallet_prelude::*,
73
            traits::{EnsureOrigin, UnixTime, ValidatorRegistration},
74
            BoundedVec, DefaultNoBound,
75
        },
76
        frame_system::pallet_prelude::*,
77
        sp_runtime::{traits::Convert, SaturatedConversion},
78
        sp_std::vec::Vec,
79
    };
80

            
81
    /// Configure the pallet by specifying the parameters and types on which it depends.
82
    #[pallet::config]
83
    pub trait Config: frame_system::Config {
84
        /// Overarching event type.
85
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
86

            
87
        /// Origin that can dictate updating parameters of this pallet.
88
        type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
89

            
90
        /// Number of eras to keep in history.
91
        ///
92
        /// Following information is kept for eras in `[current_era -
93
        /// HistoryDepth, current_era]`: `ErasStartSessionIndex`
94
        ///
95
        /// Must be more than the number of eras delayed by session.
96
        /// I.e. active era must always be in history. I.e. `active_era >
97
        /// current_era - history_depth` must be guaranteed.
98
        ///
99
        /// If migrating an existing pallet from storage value to config value,
100
        /// this should be set to same value or greater as in storage.
101
        #[pallet::constant]
102
        type HistoryDepth: Get<u32>;
103

            
104
        /// Maximum number of whitelisted validators.
105
        #[pallet::constant]
106
        type MaxWhitelistedValidators: Get<u32>;
107

            
108
        /// Maximum number of external validators.
109
        #[pallet::constant]
110
        type MaxExternalValidators: Get<u32>;
111

            
112
        /// A stable ID for a validator.
113
        type ValidatorId: Member
114
            + Parameter
115
            + Ord
116
            + MaybeSerializeDeserialize
117
            + MaxEncodedLen
118
            + TryFrom<Self::AccountId>;
119

            
120
        /// A conversion from account ID to validator ID.
121
        ///
122
        /// Its cost must be at most one storage read.
123
        type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
124

            
125
        /// Validate a user is registered
126
        type ValidatorRegistration: ValidatorRegistration<Self::ValidatorId>;
127

            
128
        /// Time used for computing era duration.
129
        ///
130
        /// It is guaranteed to start being called from the first `on_finalize`. Thus value at
131
        /// genesis is not used.
132
        type UnixTime: UnixTime;
133

            
134
        /// Number of sessions per era.
135
        #[pallet::constant]
136
        type SessionsPerEra: Get<SessionIndex>;
137

            
138
        type OnEraStart: OnEraStart;
139
        type OnEraEnd: OnEraEnd;
140

            
141
        /// The weight information of this pallet.
142
        type WeightInfo: WeightInfo;
143

            
144
        #[cfg(feature = "runtime-benchmarks")]
145
        type Currency: Currency<Self::AccountId>
146
            + frame_support::traits::fungible::Balanced<Self::AccountId>;
147
    }
148

            
149
4
    #[pallet::pallet]
150
    pub struct Pallet<T>(_);
151

            
152
    /// Fixed validators set by root/governance. Have priority over the external validators.
153
906
    #[pallet::storage]
154
    pub type WhitelistedValidators<T: Config> =
155
        StorageValue<_, BoundedVec<T::ValidatorId, T::MaxWhitelistedValidators>, ValueQuery>;
156

            
157
    /// Validators set using storage proofs from another blockchain. Ignored if `SkipExternalValidators` is true.
158
478
    #[pallet::storage]
159
    pub type ExternalValidators<T: Config> =
160
        StorageValue<_, BoundedVec<T::ValidatorId, T::MaxExternalValidators>, ValueQuery>;
161

            
162
    /// Allow to disable external validators.
163
674
    #[pallet::storage]
164
    pub type SkipExternalValidators<T: Config> = StorageValue<_, bool, ValueQuery>;
165

            
166
    /// The current era information, it is either ActiveEra or ActiveEra + 1 if the new era validators have been queued.
167
2046
    #[pallet::storage]
168
    pub type CurrentEra<T: Config> = StorageValue<_, EraIndex>;
169

            
170
    /// The active era information, it holds index and start.
171
2828
    #[pallet::storage]
172
    pub type ActiveEra<T: Config> = StorageValue<_, ActiveEraInfo>;
173

            
174
    /// The session index at which the era start for the last [`Config::HistoryDepth`] eras.
175
    ///
176
    /// Note: This tracks the starting session (i.e. session index when era start being active)
177
    /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`.
178
1440
    #[pallet::storage]
179
    pub type ErasStartSessionIndex<T> = StorageMap<_, Twox64Concat, EraIndex, SessionIndex>;
180

            
181
    /// Mode of era forcing.
182
1104
    #[pallet::storage]
183
    pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
184

            
185
    #[pallet::genesis_config]
186
    #[derive(DefaultNoBound)]
187
    pub struct GenesisConfig<T: Config> {
188
        pub skip_external_validators: bool,
189
        pub whitelisted_validators: Vec<T::ValidatorId>,
190
    }
191

            
192
110
    #[pallet::genesis_build]
193
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
194
112
        fn build(&self) {
195
112
            let duplicate_validators = self
196
112
                .whitelisted_validators
197
112
                .iter()
198
112
                // T::ValidatorId does not impl Ord or Hash so we cannot collect into set directly,
199
112
                // but we can check for duplicates if we encode them first.
200
235
                .map(|x| x.encode())
201
112
                .collect::<sp_std::collections::btree_set::BTreeSet<_>>();
202
112
            assert!(
203
112
                duplicate_validators.len() == self.whitelisted_validators.len(),
204
                "duplicate validators in genesis."
205
            );
206

            
207
112
            let bounded_validators = BoundedVec::<_, T::MaxWhitelistedValidators>::try_from(
208
112
                self.whitelisted_validators.clone(),
209
112
            )
210
112
            .expect("genesis validators are more than T::MaxWhitelistedValidators");
211
112

            
212
112
            <WhitelistedValidators<T>>::put(bounded_validators);
213
112
            <SkipExternalValidators<T>>::put(self.skip_external_validators);
214
112
        }
215
    }
216

            
217
    #[pallet::event]
218
230
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
219
    pub enum Event<T: Config> {
220
3
        /// A new whitelisted validator was added.
221
        WhitelistedValidatorAdded { account_id: T::AccountId },
222
1
        /// A whitelisted validator was removed.
223
        WhitelistedValidatorRemoved { account_id: T::AccountId },
224
        /// A new era has started.
225
        NewEra { era: EraIndex },
226
        /// A new force era mode was set.
227
        ForceEra { mode: Forcing },
228
    }
229

            
230
16
    #[pallet::error]
231
    pub enum Error<T> {
232
        /// There are too many whitelisted validators.
233
        TooManyWhitelisted,
234
        /// Account is already whitelisted.
235
        AlreadyWhitelisted,
236
        /// Account is not whitelisted.
237
        NotWhitelisted,
238
        /// Account does not have keys registered
239
        NoKeysRegistered,
240
        /// Unable to derive validator id from account id
241
        UnableToDeriveValidatorId,
242
    }
243

            
244
    #[pallet::call]
245
    impl<T: Config> Pallet<T> {
246
        /// Allow to ignore external validators and use only whitelisted ones.
247
        ///
248
        /// The origin for this call must be the `UpdateOrigin`.
249
        #[pallet::call_index(0)]
250
        #[pallet::weight(T::WeightInfo::skip_external_validators())]
251
4
        pub fn skip_external_validators(origin: OriginFor<T>, skip: bool) -> DispatchResult {
252
4
            T::UpdateOrigin::ensure_origin(origin)?;
253

            
254
4
            <SkipExternalValidators<T>>::put(skip);
255
4

            
256
4
            Ok(())
257
        }
258

            
259
        /// Add a new account `who` to the list of `WhitelistedValidators`.
260
        ///
261
        /// The origin for this call must be the `UpdateOrigin`.
262
        #[pallet::call_index(1)]
263
        #[pallet::weight(T::WeightInfo::add_whitelisted(
264
			T::MaxWhitelistedValidators::get().saturating_sub(1),
265
		))]
266
        pub fn add_whitelisted(
267
            origin: OriginFor<T>,
268
            who: T::AccountId,
269
28
        ) -> DispatchResultWithPostInfo {
270
28
            T::UpdateOrigin::ensure_origin(origin)?;
271
            // don't let one unprepared collator ruin things for everyone.
272
27
            let maybe_validator_id = T::ValidatorIdOf::convert(who.clone())
273
27
                .filter(T::ValidatorRegistration::is_registered);
274

            
275
27
            let validator_id = maybe_validator_id.ok_or(Error::<T>::NoKeysRegistered)?;
276

            
277
26
            <WhitelistedValidators<T>>::try_mutate(|whitelisted| -> DispatchResult {
278
26
                if whitelisted.contains(&validator_id) {
279
1
                    Err(Error::<T>::AlreadyWhitelisted)?;
280
25
                }
281
25
                whitelisted
282
25
                    .try_push(validator_id.clone())
283
25
                    .map_err(|_| Error::<T>::TooManyWhitelisted)?;
284
24
                Ok(())
285
26
            })?;
286

            
287
24
            Self::deposit_event(Event::WhitelistedValidatorAdded { account_id: who });
288
24

            
289
24
            let weight_used = <T as Config>::WeightInfo::add_whitelisted(
290
24
                WhitelistedValidators::<T>::decode_len()
291
24
                    .unwrap_or_default()
292
24
                    .try_into()
293
24
                    .unwrap_or(T::MaxWhitelistedValidators::get().saturating_sub(1)),
294
24
            );
295
24

            
296
24
            Ok(Some(weight_used).into())
297
        }
298

            
299
        /// Remove an account `who` from the list of `WhitelistedValidators` collators.
300
        ///
301
        /// The origin for this call must be the `UpdateOrigin`.
302
        #[pallet::call_index(2)]
303
        #[pallet::weight(T::WeightInfo::remove_whitelisted(T::MaxWhitelistedValidators::get()))]
304
12
        pub fn remove_whitelisted(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
305
12
            T::UpdateOrigin::ensure_origin(origin)?;
306

            
307
11
            let validator_id = T::ValidatorIdOf::convert(who.clone())
308
11
                .ok_or(Error::<T>::UnableToDeriveValidatorId)?;
309

            
310
11
            <WhitelistedValidators<T>>::try_mutate(|whitelisted| -> DispatchResult {
311
11
                let pos = whitelisted
312
11
                    .iter()
313
14
                    .position(|x| x == &validator_id)
314
11
                    .ok_or(Error::<T>::NotWhitelisted)?;
315
10
                whitelisted.remove(pos);
316
10
                Ok(())
317
11
            })?;
318

            
319
10
            Self::deposit_event(Event::WhitelistedValidatorRemoved { account_id: who });
320
10
            Ok(())
321
        }
322

            
323
        /// Force when the next era will start. Possible values: next session, never, same as always.
324
        #[pallet::call_index(3)]
325
        #[pallet::weight(T::WeightInfo::force_era())]
326
3
        pub fn force_era(origin: OriginFor<T>, mode: Forcing) -> DispatchResult {
327
3
            T::UpdateOrigin::ensure_origin(origin)?;
328
3
            Self::set_force_era(mode);
329
3
            Ok(())
330
        }
331
    }
332

            
333
    impl<T: Config> Pallet<T> {
334
21
        pub fn set_external_validators(validators: Vec<T::ValidatorId>) -> DispatchResult {
335
21
            // If more validators than max, take the first n
336
21
            let validators = BoundedVec::truncate_from(validators);
337
21
            <ExternalValidators<T>>::put(validators);
338
21

            
339
21
            Ok(())
340
21
        }
341

            
342
        /// Helper to set a new `ForceEra` mode.
343
4
        pub(crate) fn set_force_era(mode: Forcing) {
344
4
            log::info!("Setting force era mode {:?}.", mode);
345
4
            ForceEra::<T>::put(mode);
346
4
            Self::deposit_event(Event::<T>::ForceEra { mode });
347
4
        }
348

            
349
24
        pub fn whitelisted_validators() -> Vec<T::ValidatorId> {
350
24
            <WhitelistedValidators<T>>::get().into()
351
24
        }
352

            
353
772
        pub fn active_era() -> Option<ActiveEraInfo> {
354
772
            <ActiveEra<T>>::get()
355
772
        }
356

            
357
585
        pub fn current_era() -> Option<EraIndex> {
358
585
            <CurrentEra<T>>::get()
359
585
        }
360

            
361
1211
        pub fn eras_start_session_index(era: EraIndex) -> Option<u32> {
362
1211
            <ErasStartSessionIndex<T>>::get(era)
363
1211
        }
364

            
365
        /// Returns validators for the next session. Whitelisted validators first, then external validators.
366
        /// The returned list is deduplicated, but the order is respected.
367
        /// If `SkipExternalValidators` is true, this function will ignore external validators.
368
221
        pub fn validators() -> Vec<T::ValidatorId> {
369
221
            let mut validators: Vec<_> = WhitelistedValidators::<T>::get().into();
370
221

            
371
221
            if !SkipExternalValidators::<T>::get() {
372
218
                validators.extend(ExternalValidators::<T>::get())
373
3
            }
374

            
375
221
            remove_duplicates(validators)
376
221
        }
377

            
378
        /// Plan a new session potentially trigger a new era.
379
553
        pub(crate) fn new_session(session_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
380
553
            if let Some(current_era) = Self::current_era() {
381
                // Initial era has been set.
382
441
                let current_era_start_session_index = Self::eras_start_session_index(current_era)
383
441
                    .unwrap_or_else(|| {
384
                        frame_support::print(
385
                            "Error: start_session_index must be set for current_era",
386
                        );
387
                        0
388
441
                    });
389
441

            
390
441
                let era_length = session_index.saturating_sub(current_era_start_session_index); // Must never happen.
391

            
392
433
                match ForceEra::<T>::get() {
393
                    // Will be set to `NotForcing` again if a new era has been triggered.
394
1
                    Forcing::ForceNew => (),
395
                    // Short circuit to `try_trigger_new_era`.
396
4
                    Forcing::ForceAlways => (),
397
                    // Only go to `try_trigger_new_era` if deadline reached.
398
433
                    Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (),
399
                    _ => {
400
                        // Either `Forcing::ForceNone`,
401
                        // or `Forcing::NotForcing if era_length < T::SessionsPerEra::get()`.
402
334
                        return None;
403
                    }
404
                }
405

            
406
                // New era.
407
107
                let maybe_new_era_validators = Self::try_trigger_new_era(session_index);
408
107
                if maybe_new_era_validators.is_some()
409
107
                    && matches!(ForceEra::<T>::get(), Forcing::ForceNew)
410
1
                {
411
1
                    Self::set_force_era(Forcing::NotForcing);
412
106
                }
413

            
414
107
                maybe_new_era_validators
415
            } else {
416
                // Set initial era.
417
112
                log!(log::Level::Debug, "Starting the first era.");
418
112
                Self::try_trigger_new_era(session_index)
419
            }
420
553
        }
421

            
422
        /// Start a session potentially starting an era.
423
441
        pub(crate) fn start_session(start_session: SessionIndex) {
424
441
            let next_active_era = Self::active_era().map(|e| e.index + 1).unwrap_or(0);
425
            // This is only `Some` when current era has already progressed to the next era, while the
426
            // active era is one behind (i.e. in the *last session of the active era*, or *first session
427
            // of the new current era*, depending on how you look at it).
428
192
            if let Some(next_active_era_start_session_index) =
429
441
                Self::eras_start_session_index(next_active_era)
430
            {
431
192
                if next_active_era_start_session_index == start_session {
432
192
                    Self::start_era(start_session);
433
192
                } else if next_active_era_start_session_index < start_session {
434
                    // This arm should never happen, but better handle it than to stall the pallet.
435
                    frame_support::print("Warning: A session appears to have been skipped.");
436
                    Self::start_era(start_session);
437
                }
438
249
            }
439
441
        }
440

            
441
        /// End a session potentially ending an era.
442
329
        pub(crate) fn end_session(session_index: SessionIndex) {
443
329
            if let Some(active_era) = Self::active_era() {
444
80
                if let Some(next_active_era_start_session_index) =
445
329
                    Self::eras_start_session_index(active_era.index + 1)
446
                {
447
80
                    if next_active_era_start_session_index == session_index + 1 {
448
80
                        Self::end_era(active_era, session_index);
449
80
                    }
450
249
                }
451
            }
452
329
        }
453

            
454
        /// Start a new era. It does:
455
        /// * Increment `active_era.index`,
456
        /// * reset `active_era.start`,
457
        /// * emit `NewEra` event,
458
        /// * call `OnEraStart` hook,
459
192
        pub(crate) fn start_era(start_session: SessionIndex) {
460
192
            let active_era = ActiveEra::<T>::mutate(|active_era| {
461
192
                let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
462
192
                *active_era = Some(ActiveEraInfo {
463
192
                    index: new_index,
464
192
                    // Set new active era start in next `on_finalize`. To guarantee usage of `Time`
465
192
                    start: None,
466
192
                });
467
192
                new_index
468
192
            });
469
192
            Self::deposit_event(Event::NewEra { era: active_era });
470
192
            T::OnEraStart::on_era_start(active_era, start_session);
471
192
        }
472

            
473
        /// End era. It does:
474
        /// * call `OnEraEnd` hook,
475
80
        pub(crate) fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) {
476
80
            // Note: active_era.start can be None if end era is called during genesis config.
477
80
            T::OnEraEnd::on_era_end(active_era.index);
478
80
        }
479

            
480
        /// Plan a new era.
481
        ///
482
        /// * Bump the current era storage (which holds the latest planned era).
483
        /// * Store start session index for the new planned era.
484
        /// * Clean old era information.
485
        ///
486
        /// Returns the new validator set.
487
219
        pub fn trigger_new_era(start_session_index: SessionIndex) -> Vec<T::ValidatorId> {
488
219
            // Increment or set current era.
489
219
            let new_planned_era = CurrentEra::<T>::mutate(|s| {
490
219
                *s = Some(s.map(|s| s + 1).unwrap_or(0));
491
219
                s.unwrap()
492
219
            });
493
219
            ErasStartSessionIndex::<T>::insert(&new_planned_era, &start_session_index);
494

            
495
            // Clean old era information.
496
219
            if let Some(old_era) = new_planned_era.checked_sub(T::HistoryDepth::get() + 1) {
497
                Self::clear_era_information(old_era);
498
219
            }
499

            
500
            // Returns new validators
501
219
            Self::validators()
502
219
        }
503

            
504
        /// Potentially plan a new era.
505
        ///
506
        /// In case a new era is planned, the new validator set is returned.
507
219
        pub(crate) fn try_trigger_new_era(
508
219
            start_session_index: SessionIndex,
509
219
        ) -> Option<Vec<T::ValidatorId>> {
510
219
            Some(Self::trigger_new_era(start_session_index))
511
219
        }
512

            
513
        /// Clear all era information for given era.
514
        pub(crate) fn clear_era_information(era_index: EraIndex) {
515
            ErasStartSessionIndex::<T>::remove(era_index);
516
        }
517
    }
518

            
519
460
    #[pallet::hooks]
520
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
521
229
        fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
522
229
            // just return the weight of the on_finalize.
523
229
            T::DbWeight::get().reads(1)
524
229
        }
525

            
526
229
        fn on_finalize(_n: BlockNumberFor<T>) {
527
            // Set the start of the first era.
528
229
            if let Some(mut active_era) = <ActiveEra<T>>::get() {
529
229
                if active_era.start.is_none() {
530
16
                    let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
531
16
                    active_era.start = Some(now_as_millis_u64);
532
16
                    // This write only ever happens once, we don't include it in the weight in
533
16
                    // general
534
16
                    ActiveEra::<T>::put(active_era);
535
213
                }
536
            }
537
            // `on_finalize` weight is tracked in `on_initialize`
538
229
        }
539
    }
540
}
541

            
542
/// Keeps only the first instance of each element in the input vec. Respects ordering of elements.
543
221
fn remove_duplicates<T: Ord + Clone>(input: Vec<T>) -> Vec<T> {
544
221
    let mut seen = BTreeSet::new();
545
221
    let mut result = Vec::with_capacity(input.len());
546

            
547
785
    for item in input {
548
564
        if seen.insert(item.clone()) {
549
559
            result.push(item);
550
559
        }
551
    }
552

            
553
221
    result
554
221
}
555

            
556
impl<T: Config> pallet_session::SessionManager<T::ValidatorId> for Pallet<T> {
557
529
    fn new_session(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
558
529
        log!(log::Level::Trace, "planning new session {}", new_index);
559
529
        Self::new_session(new_index)
560
529
    }
561
24
    fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
562
24
        log!(
563
24
            log::Level::Trace,
564
            "planning new session {} at genesis",
565
            new_index
566
        );
567
24
        Self::new_session(new_index)
568
24
    }
569
441
    fn start_session(start_index: SessionIndex) {
570
441
        log!(log::Level::Trace, "starting session {}", start_index);
571
441
        Self::start_session(start_index)
572
441
    }
573
329
    fn end_session(end_index: SessionIndex) {
574
329
        log!(log::Level::Trace, "ending session {}", end_index);
575
329
        Self::end_session(end_index)
576
329
    }
577
}
578

            
579
impl<T: Config> pallet_session::historical::SessionManager<T::ValidatorId, ()> for Pallet<T> {
580
485
    fn new_session(new_index: SessionIndex) -> Option<Vec<(T::ValidatorId, ())>> {
581
485
        <Self as pallet_session::SessionManager<_>>::new_session(new_index)
582
511
            .map(|r| r.into_iter().map(|v| (v, Default::default())).collect())
583
485
    }
584

            
585
385
    fn start_session(start_index: SessionIndex) {
586
385
        <Self as pallet_session::SessionManager<_>>::start_session(start_index)
587
385
    }
588

            
589
285
    fn end_session(end_index: SessionIndex) {
590
285
        <Self as pallet_session::SessionManager<_>>::end_session(end_index)
591
285
    }
592
}
593

            
594
impl<T: Config> EraIndexProvider for Pallet<T> {
595
13
    fn active_era() -> ActiveEraInfo {
596
13
        <ActiveEra<T>>::get().unwrap_or(ActiveEraInfo {
597
13
            index: 0,
598
13
            start: None,
599
13
        })
600
13
    }
601

            
602
10
    fn era_to_session_start(era_index: EraIndex) -> Option<u32> {
603
10
        <ErasStartSessionIndex<T>>::get(era_index)
604
10
    }
605
}
606

            
607
impl<T: Config> ValidatorProvider<T::ValidatorId> for Pallet<T> {
608
1
    fn validators() -> Vec<T::ValidatorId> {
609
1
        Self::validators()
610
1
    }
611
}
612

            
613
impl<T: Config> InvulnerablesProvider<T::ValidatorId> for Pallet<T> {
614
10
    fn invulnerables() -> Vec<T::ValidatorId> {
615
10
        Self::whitelisted_validators()
616
10
    }
617
}
618

            
619
/// Mode of era-forcing.
620
#[derive(
621
    Copy, Clone, PartialEq, Eq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
622
)]
623
pub enum Forcing {
624
5
    /// Not forcing anything - just let whatever happen.
625
    #[default]
626
    NotForcing,
627
2
    /// Force a new era on the next session start, then reset to `NotForcing` as soon as it is done.
628
    ForceNew,
629
3
    /// Avoid a new era indefinitely.
630
    ForceNone,
631
8
    /// Force a new era at the end of all sessions indefinitely.
632
    ForceAlways,
633
}