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
//!      Are not rewarded.
25
//! - ExternalValidators: Validators set using storage proofs from another blockchain. Can be disabled by setting
26
//!     `SkipExternalValidators` to true.
27
//!
28
//! Validators only change once per era. By default the era changes after a fixed number of sessions, but new eras
29
//! can be forced or disabled using a root extrinsic.
30
//!
31
//! The structure of this pallet and the concept of eras is inspired by `pallet_staking` from Polkadot.
32

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
137
        type OnEraStart: OnEraStart;
138
        type OnEraEnd: OnEraEnd;
139

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

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

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

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

            
156
    /// Copy of `WhitelistedValidators` at the start of this active era.
157
    /// Used to check which validators we don't need to reward.
158
8082
    #[pallet::storage]
159
    pub type WhitelistedValidatorsActiveEra<T: Config> =
160
        StorageValue<_, BoundedVec<T::ValidatorId, T::MaxWhitelistedValidators>, ValueQuery>;
161

            
162
    /// Same as `WhitelistedValidatorsActiveEra` but only exists for a brief period of time when the
163
    /// next era has been planned but not enacted yet.
164
1512
    #[pallet::storage]
165
    pub type WhitelistedValidatorsActiveEraPending<T: Config> =
166
        StorageValue<_, BoundedVec<T::ValidatorId, T::MaxWhitelistedValidators>, ValueQuery>;
167

            
168
    /// Validators set using storage proofs from another blockchain. Ignored if `SkipExternalValidators` is true.
169
1052
    #[pallet::storage]
170
    pub type ExternalValidators<T: Config> =
171
        StorageValue<_, BoundedVec<T::ValidatorId, T::MaxExternalValidators>, ValueQuery>;
172

            
173
    /// Allow to disable external validators.
174
1006
    #[pallet::storage]
175
    pub type SkipExternalValidators<T: Config> = StorageValue<_, bool, ValueQuery>;
176

            
177
    /// The current era information, it is either ActiveEra or ActiveEra + 1 if the new era validators have been queued.
178
2828
    #[pallet::storage]
179
    pub type CurrentEra<T: Config> = StorageValue<_, EraIndex>;
180

            
181
    /// The active era information, it holds index and start.
182
12030
    #[pallet::storage]
183
    pub type ActiveEra<T: Config> = StorageValue<_, ActiveEraInfo>;
184

            
185
    /// The session index at which the era start for the last [`Config::HistoryDepth`] eras.
186
    ///
187
    /// Note: This tracks the starting session (i.e. session index when era start being active)
188
    /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`.
189
2188
    #[pallet::storage]
190
    pub type ErasStartSessionIndex<T> = StorageMap<_, Twox64Concat, EraIndex, SessionIndex>;
191

            
192
    /// Mode of era forcing.
193
1424
    #[pallet::storage]
194
    pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
195

            
196
    /// Latest received external index. This index can be a timestamp
197
    /// a set-id, an epoch or in general anything that identifies
198
    /// a particular set of validators selected at a given point in time
199
682
    #[pallet::storage]
200
    pub type ExternalIndex<T> = StorageValue<_, u64, ValueQuery>;
201

            
202
    /// Pending external index to be applied in the upcoming era
203
1156
    #[pallet::storage]
204
    pub type PendingExternalIndex<T> = StorageValue<_, u64, ValueQuery>;
205

            
206
    /// Current external index attached to the latest validators
207
584
    #[pallet::storage]
208
    pub type CurrentExternalIndex<T> = StorageValue<_, u64, ValueQuery>;
209

            
210
    #[pallet::genesis_config]
211
    #[derive(DefaultNoBound)]
212
    pub struct GenesisConfig<T: Config> {
213
        pub skip_external_validators: bool,
214
        pub whitelisted_validators: Vec<T::ValidatorId>,
215
        pub external_validators: Vec<T::ValidatorId>,
216
    }
217

            
218
176
    #[pallet::genesis_build]
219
    impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
220
178
        fn build(&self) {
221
178
            let duplicate_validators = self
222
178
                .whitelisted_validators
223
178
                .iter()
224
178
                // T::ValidatorId does not impl Ord or Hash so we cannot collect into set directly,
225
178
                // but we can check for duplicates if we encode them first.
226
355
                .map(|x| x.encode())
227
178
                .collect::<sp_std::collections::btree_set::BTreeSet<_>>();
228
178
            assert!(
229
178
                duplicate_validators.len() == self.whitelisted_validators.len(),
230
                "duplicate validators in genesis."
231
            );
232

            
233
178
            let bounded_validators = BoundedVec::<_, T::MaxWhitelistedValidators>::try_from(
234
178
                self.whitelisted_validators.clone(),
235
178
            )
236
178
            .expect("genesis validators are more than T::MaxWhitelistedValidators");
237
178

            
238
178
            let bounded_external_validators = BoundedVec::<_, T::MaxExternalValidators>::try_from(
239
178
                self.external_validators.clone(),
240
178
            )
241
178
            .expect("genesis external validators are more than T::MaxExternalValidators");
242
178

            
243
178
            <SkipExternalValidators<T>>::put(self.skip_external_validators);
244
178
            <WhitelistedValidators<T>>::put(&bounded_validators);
245
178
            <WhitelistedValidatorsActiveEra<T>>::put(&bounded_validators);
246
178
            <WhitelistedValidatorsActiveEraPending<T>>::put(&bounded_validators);
247
178
            <ExternalValidators<T>>::put(&bounded_external_validators);
248
178
        }
249
    }
250

            
251
    #[pallet::event]
252
348
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
253
    pub enum Event<T: Config> {
254
3
        /// A new whitelisted validator was added.
255
        WhitelistedValidatorAdded { account_id: T::AccountId },
256
1
        /// A whitelisted validator was removed.
257
        WhitelistedValidatorRemoved { account_id: T::AccountId },
258
6
        /// A new era has started.
259
        NewEra { era: EraIndex },
260
        /// A new force era mode was set.
261
        ForceEra { mode: Forcing },
262
3
        /// External validators were set.
263
        ExternalValidatorsSet {
264
            validators: Vec<T::ValidatorId>,
265
            external_index: u64,
266
        },
267
    }
268

            
269
16
    #[pallet::error]
270
    pub enum Error<T> {
271
        /// There are too many whitelisted validators.
272
        TooManyWhitelisted,
273
        /// Account is already whitelisted.
274
        AlreadyWhitelisted,
275
        /// Account is not whitelisted.
276
        NotWhitelisted,
277
        /// Account does not have keys registered
278
        NoKeysRegistered,
279
        /// Unable to derive validator id from account id
280
        UnableToDeriveValidatorId,
281
    }
282

            
283
    #[pallet::call]
284
    impl<T: Config> Pallet<T> {
285
        /// Allow to ignore external validators and use only whitelisted ones.
286
        ///
287
        /// The origin for this call must be the `UpdateOrigin`.
288
        #[pallet::call_index(0)]
289
        #[pallet::weight(T::WeightInfo::skip_external_validators())]
290
7
        pub fn skip_external_validators(origin: OriginFor<T>, skip: bool) -> DispatchResult {
291
7
            T::UpdateOrigin::ensure_origin(origin)?;
292

            
293
7
            <SkipExternalValidators<T>>::put(skip);
294
7

            
295
7
            Ok(())
296
        }
297

            
298
        /// Add a new account `who` to the list of `WhitelistedValidators`.
299
        ///
300
        /// The origin for this call must be the `UpdateOrigin`.
301
        #[pallet::call_index(1)]
302
        #[pallet::weight(T::WeightInfo::add_whitelisted(
303
			T::MaxWhitelistedValidators::get().saturating_sub(1),
304
		))]
305
        pub fn add_whitelisted(
306
            origin: OriginFor<T>,
307
            who: T::AccountId,
308
29
        ) -> DispatchResultWithPostInfo {
309
29
            T::UpdateOrigin::ensure_origin(origin)?;
310
            // don't let one unprepared validator ruin things for everyone.
311
28
            let maybe_validator_id = T::ValidatorIdOf::convert(who.clone())
312
28
                .filter(T::ValidatorRegistration::is_registered);
313

            
314
28
            let validator_id = maybe_validator_id.ok_or(Error::<T>::NoKeysRegistered)?;
315

            
316
27
            <WhitelistedValidators<T>>::try_mutate(|whitelisted| -> DispatchResult {
317
27
                if whitelisted.contains(&validator_id) {
318
1
                    Err(Error::<T>::AlreadyWhitelisted)?;
319
26
                }
320
26
                whitelisted
321
26
                    .try_push(validator_id.clone())
322
26
                    .map_err(|_| Error::<T>::TooManyWhitelisted)?;
323
25
                Ok(())
324
27
            })?;
325

            
326
25
            Self::deposit_event(Event::WhitelistedValidatorAdded { account_id: who });
327
25

            
328
25
            let weight_used = <T as Config>::WeightInfo::add_whitelisted(
329
25
                WhitelistedValidators::<T>::decode_len()
330
25
                    .unwrap_or_default()
331
25
                    .try_into()
332
25
                    .unwrap_or(T::MaxWhitelistedValidators::get().saturating_sub(1)),
333
25
            );
334
25

            
335
25
            Ok(Some(weight_used).into())
336
        }
337

            
338
        /// Remove an account `who` from the list of `WhitelistedValidators` collators.
339
        ///
340
        /// The origin for this call must be the `UpdateOrigin`.
341
        #[pallet::call_index(2)]
342
        #[pallet::weight(T::WeightInfo::remove_whitelisted(T::MaxWhitelistedValidators::get()))]
343
18
        pub fn remove_whitelisted(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
344
18
            T::UpdateOrigin::ensure_origin(origin)?;
345

            
346
17
            let validator_id = T::ValidatorIdOf::convert(who.clone())
347
17
                .ok_or(Error::<T>::UnableToDeriveValidatorId)?;
348

            
349
17
            <WhitelistedValidators<T>>::try_mutate(|whitelisted| -> DispatchResult {
350
17
                let pos = whitelisted
351
17
                    .iter()
352
20
                    .position(|x| x == &validator_id)
353
17
                    .ok_or(Error::<T>::NotWhitelisted)?;
354
16
                whitelisted.remove(pos);
355
16
                Ok(())
356
17
            })?;
357

            
358
16
            Self::deposit_event(Event::WhitelistedValidatorRemoved { account_id: who });
359
16
            Ok(())
360
        }
361

            
362
        /// Force when the next era will start. Possible values: next session, never, same as always.
363
        #[pallet::call_index(3)]
364
        #[pallet::weight(T::WeightInfo::force_era())]
365
3
        pub fn force_era(origin: OriginFor<T>, mode: Forcing) -> DispatchResult {
366
3
            T::UpdateOrigin::ensure_origin(origin)?;
367
3
            Self::set_force_era(mode);
368
3
            Ok(())
369
        }
370

            
371
        /// Manually set external validators. Should only be needed for tests, validators are set
372
        /// automatically by the bridge.
373
        #[pallet::call_index(4)]
374
        #[pallet::weight(T::WeightInfo::set_external_validators())]
375
        pub fn set_external_validators(
376
            origin: OriginFor<T>,
377
            validators: Vec<T::ValidatorId>,
378
            external_index: u64,
379
        ) -> DispatchResult {
380
            T::UpdateOrigin::ensure_origin(origin)?;
381

            
382
            Self::set_external_validators_inner(validators, external_index)
383
        }
384
    }
385

            
386
    impl<T: Config> Pallet<T> {
387
33
        pub fn set_external_validators_inner(
388
33
            validators: Vec<T::ValidatorId>,
389
33
            external_index: u64,
390
33
        ) -> DispatchResult {
391
33
            // If more validators than max, take the first n
392
33
            let validators = BoundedVec::truncate_from(validators);
393
33
            <ExternalValidators<T>>::put(&validators);
394
33
            <ExternalIndex<T>>::put(external_index);
395
33

            
396
33
            Self::deposit_event(Event::<T>::ExternalValidatorsSet {
397
33
                validators: validators.into_inner(),
398
33
                external_index,
399
33
            });
400
33
            Ok(())
401
33
        }
402

            
403
        /// Helper to set a new `ForceEra` mode.
404
4
        pub(crate) fn set_force_era(mode: Forcing) {
405
4
            log::info!("Setting force era mode {:?}.", mode);
406
4
            ForceEra::<T>::put(mode);
407
4
            Self::deposit_event(Event::<T>::ForceEra { mode });
408
4
        }
409

            
410
37
        pub fn whitelisted_validators() -> Vec<T::ValidatorId> {
411
37
            <WhitelistedValidators<T>>::get().into()
412
37
        }
413

            
414
980
        pub fn active_era() -> Option<ActiveEraInfo> {
415
980
            <ActiveEra<T>>::get()
416
980
        }
417

            
418
798
        pub fn current_era() -> Option<EraIndex> {
419
798
            <CurrentEra<T>>::get()
420
798
        }
421

            
422
1556
        pub fn eras_start_session_index(era: EraIndex) -> Option<u32> {
423
1556
            <ErasStartSessionIndex<T>>::get(era)
424
1556
        }
425

            
426
        /// Returns validators for the next session. Whitelisted validators first, then external validators.
427
        /// The returned list is deduplicated, but the order is respected.
428
        /// If `SkipExternalValidators` is true, this function will ignore external validators.
429
318
        pub fn validators() -> Vec<T::ValidatorId> {
430
318
            let mut validators: Vec<_> = WhitelistedValidators::<T>::get().into();
431
318

            
432
318
            if !SkipExternalValidators::<T>::get() {
433
315
                validators.extend(ExternalValidators::<T>::get())
434
3
            }
435

            
436
318
            remove_duplicates(validators)
437
318
        }
438

            
439
        /// Plan a new session potentially trigger a new era.
440
756
        pub(crate) fn new_session(session_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
441
756
            if let Some(current_era) = Self::current_era() {
442
                // Initial era has been set.
443
578
                let current_era_start_session_index = Self::eras_start_session_index(current_era)
444
578
                    .unwrap_or_else(|| {
445
                        frame_support::print(
446
                            "Error: start_session_index must be set for current_era",
447
                        );
448
                        0
449
578
                    });
450
578

            
451
578
                let era_length = session_index.saturating_sub(current_era_start_session_index); // Must never happen.
452

            
453
570
                match ForceEra::<T>::get() {
454
                    // Will be set to `NotForcing` again if a new era has been triggered.
455
1
                    Forcing::ForceNew => (),
456
                    // Short circuit to `try_trigger_new_era`.
457
4
                    Forcing::ForceAlways => (),
458
                    // Only go to `try_trigger_new_era` if deadline reached.
459
570
                    Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (),
460
                    _ => {
461
                        // Either `Forcing::ForceNone`,
462
                        // or `Forcing::NotForcing if era_length < T::SessionsPerEra::get()`.
463
448
                        return None;
464
                    }
465
                }
466

            
467
                // New era.
468
130
                let maybe_new_era_validators = Self::try_trigger_new_era(session_index);
469
130
                if maybe_new_era_validators.is_some()
470
130
                    && matches!(ForceEra::<T>::get(), Forcing::ForceNew)
471
1
                {
472
1
                    Self::set_force_era(Forcing::NotForcing);
473
129
                }
474

            
475
130
                maybe_new_era_validators
476
            } else {
477
                // Set initial era.
478
178
                log!(log::Level::Debug, "Starting the first era.");
479
178
                Self::try_trigger_new_era(session_index)
480
            }
481
756
        }
482

            
483
        /// Start a session potentially starting an era.
484
578
        pub(crate) fn start_session(start_session: SessionIndex) {
485
578
            let next_active_era = Self::active_era().map(|e| e.index + 1).unwrap_or(0);
486
            // This is only `Some` when current era has already progressed to the next era, while the
487
            // active era is one behind (i.e. in the *last session of the active era*, or *first session
488
            // of the new current era*, depending on how you look at it).
489
270
            if let Some(next_active_era_start_session_index) =
490
578
                Self::eras_start_session_index(next_active_era)
491
            {
492
270
                if next_active_era_start_session_index == start_session {
493
270
                    Self::start_era(start_session);
494
270
                } else if next_active_era_start_session_index < start_session {
495
                    // This arm should never happen, but better handle it than to stall the pallet.
496
                    frame_support::print("Warning: A session appears to have been skipped.");
497
                    Self::start_era(start_session);
498
                }
499
308
            }
500
578
        }
501

            
502
        /// End a session potentially ending an era.
503
400
        pub(crate) fn end_session(session_index: SessionIndex) {
504
400
            if let Some(active_era) = Self::active_era() {
505
92
                if let Some(next_active_era_start_session_index) =
506
400
                    Self::eras_start_session_index(active_era.index + 1)
507
                {
508
92
                    if next_active_era_start_session_index == session_index + 1 {
509
92
                        Self::end_era(active_era, session_index);
510
92
                    }
511
308
                }
512
            }
513
400
        }
514

            
515
        /// Start a new era. It does:
516
        /// * Increment `active_era.index`,
517
        /// * reset `active_era.start`,
518
        /// * emit `NewEra` event,
519
        /// * call `OnEraStart` hook,
520
270
        pub(crate) fn start_era(start_session: SessionIndex) {
521
270
            let active_era = ActiveEra::<T>::mutate(|active_era| {
522
270
                let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
523
270
                *active_era = Some(ActiveEraInfo {
524
270
                    index: new_index,
525
270
                    // Set new active era start in next `on_finalize`. To guarantee usage of `Time`
526
270
                    start: None,
527
270
                });
528
270
                new_index
529
270
            });
530
270
            WhitelistedValidatorsActiveEra::<T>::put(
531
270
                WhitelistedValidatorsActiveEraPending::<T>::take(),
532
270
            );
533
270
            let external_idx = PendingExternalIndex::<T>::take();
534
270
            CurrentExternalIndex::<T>::put(external_idx);
535
270
            Self::deposit_event(Event::NewEra { era: active_era });
536
270
            T::OnEraStart::on_era_start(active_era, start_session, external_idx);
537
270
        }
538

            
539
        /// End era. It does:
540
        /// * call `OnEraEnd` hook,
541
92
        pub(crate) fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) {
542
92
            // Note: active_era.start can be None if end era is called during genesis config.
543
92
            T::OnEraEnd::on_era_end(active_era.index);
544
92
        }
545

            
546
        /// Plan a new era.
547
        ///
548
        /// * Bump the current era storage (which holds the latest planned era).
549
        /// * Store start session index for the new planned era.
550
        /// * Clean old era information.
551
        ///
552
        /// Returns the new validator set.
553
308
        pub fn trigger_new_era(start_session_index: SessionIndex) -> Vec<T::ValidatorId> {
554
308
            // Increment or set current era.
555
308
            let new_planned_era = CurrentEra::<T>::mutate(|s| {
556
308
                *s = Some(s.map(|s| s + 1).unwrap_or(0));
557
308
                s.unwrap()
558
308
            });
559
308
            ErasStartSessionIndex::<T>::insert(&new_planned_era, &start_session_index);
560

            
561
            // Clean old era information.
562
308
            if let Some(old_era) = new_planned_era.checked_sub(T::HistoryDepth::get() + 1) {
563
                Self::clear_era_information(old_era);
564
308
            }
565

            
566
            // Save whitelisted validators for when the era truly changes (start_era)
567
308
            WhitelistedValidatorsActiveEraPending::<T>::put(WhitelistedValidators::<T>::get());
568
308
            // Save the external index for when the era truly changes (start_era)
569
308
            PendingExternalIndex::<T>::put(ExternalIndex::<T>::get());
570
308

            
571
308
            // Returns new validators
572
308
            Self::validators()
573
308
        }
574

            
575
        /// Potentially plan a new era.
576
        ///
577
        /// In case a new era is planned, the new validator set is returned.
578
308
        pub(crate) fn try_trigger_new_era(
579
308
            start_session_index: SessionIndex,
580
308
        ) -> Option<Vec<T::ValidatorId>> {
581
308
            Some(Self::trigger_new_era(start_session_index))
582
308
        }
583

            
584
        /// Clear all era information for given era.
585
        pub(crate) fn clear_era_information(era_index: EraIndex) {
586
            ErasStartSessionIndex::<T>::remove(era_index);
587
        }
588
    }
589

            
590
668
    #[pallet::hooks]
591
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
592
333
        fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
593
333
            // just return the weight of the on_finalize.
594
333
            T::DbWeight::get().reads(1)
595
333
        }
596

            
597
333
        fn on_finalize(_n: BlockNumberFor<T>) {
598
            // Set the start of the first era.
599
333
            if let Some(mut active_era) = <ActiveEra<T>>::get() {
600
333
                if active_era.start.is_none() {
601
23
                    let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
602
23
                    active_era.start = Some(now_as_millis_u64);
603
23
                    // This write only ever happens once, we don't include it in the weight in
604
23
                    // general
605
23
                    ActiveEra::<T>::put(active_era);
606
310
                }
607
            }
608
            // `on_finalize` weight is tracked in `on_initialize`
609
333
        }
610
    }
611

            
612
    impl<T: Config> ExternalIndexProvider for Pallet<T> {
613
22
        fn get_external_index() -> u64 {
614
22
            CurrentExternalIndex::<T>::get()
615
22
        }
616
    }
617
}
618

            
619
/// Keeps only the first instance of each element in the input vec. Respects ordering of elements.
620
318
fn remove_duplicates<T: Ord + Clone>(input: Vec<T>) -> Vec<T> {
621
318
    let mut seen = BTreeSet::new();
622
318
    let mut result = Vec::with_capacity(input.len());
623

            
624
1142
    for item in input {
625
824
        if seen.insert(item.clone()) {
626
818
            result.push(item);
627
818
        }
628
    }
629

            
630
318
    result
631
318
}
632

            
633
impl<T: Config> pallet_session::SessionManager<T::ValidatorId> for Pallet<T> {
634
724
    fn new_session(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
635
724
        log!(log::Level::Trace, "planning new session {}", new_index);
636
724
        Self::new_session(new_index)
637
724
    }
638
32
    fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
639
32
        log!(
640
32
            log::Level::Trace,
641
            "planning new session {} at genesis",
642
            new_index
643
        );
644
32
        Self::new_session(new_index)
645
32
    }
646
578
    fn start_session(start_index: SessionIndex) {
647
578
        log!(log::Level::Trace, "starting session {}", start_index);
648
578
        Self::start_session(start_index)
649
578
    }
650
400
    fn end_session(end_index: SessionIndex) {
651
400
        log!(log::Level::Trace, "ending session {}", end_index);
652
400
        Self::end_session(end_index)
653
400
    }
654
}
655

            
656
impl<T: Config> pallet_session::historical::SessionManager<T::ValidatorId, ()> for Pallet<T> {
657
660
    fn new_session(new_index: SessionIndex) -> Option<Vec<(T::ValidatorId, ())>> {
658
660
        <Self as pallet_session::SessionManager<_>>::new_session(new_index)
659
679
            .map(|r| r.into_iter().map(|v| (v, Default::default())).collect())
660
660
    }
661

            
662
498
    fn start_session(start_index: SessionIndex) {
663
498
        <Self as pallet_session::SessionManager<_>>::start_session(start_index)
664
498
    }
665

            
666
336
    fn end_session(end_index: SessionIndex) {
667
336
        <Self as pallet_session::SessionManager<_>>::end_session(end_index)
668
336
    }
669
}
670

            
671
impl<T: Config> EraIndexProvider for Pallet<T> {
672
4139
    fn active_era() -> ActiveEraInfo {
673
4139
        <ActiveEra<T>>::get().unwrap_or(ActiveEraInfo {
674
4139
            index: 0,
675
4139
            start: None,
676
4139
        })
677
4139
    }
678

            
679
324
    fn era_to_session_start(era_index: EraIndex) -> Option<u32> {
680
324
        <ErasStartSessionIndex<T>>::get(era_index)
681
324
    }
682
}
683

            
684
impl<T: Config> ValidatorProvider<T::ValidatorId> for Pallet<T> {
685
1
    fn validators() -> Vec<T::ValidatorId> {
686
1
        Self::validators()
687
1
    }
688
}
689

            
690
impl<T: Config> InvulnerablesProvider<T::ValidatorId> for Pallet<T> {
691
12
    fn invulnerables() -> Vec<T::ValidatorId> {
692
12
        Self::whitelisted_validators()
693
12
    }
694
}
695

            
696
/// Mode of era-forcing.
697
#[derive(
698
    Copy, Clone, PartialEq, Eq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
699
)]
700
pub enum Forcing {
701
5
    /// Not forcing anything - just let whatever happen.
702
    #[default]
703
    NotForcing,
704
2
    /// Force a new era on the next session start, then reset to `NotForcing` as soon as it is done.
705
    ForceNew,
706
3
    /// Avoid a new era indefinitely.
707
    ForceNone,
708
8
    /// Force a new era at the end of all sessions indefinitely.
709
    ForceAlways,
710
}