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,
42
    sp_runtime::RuntimeDebug,
43
    sp_staking::SessionIndex,
44
    sp_std::collections::btree_set::BTreeSet,
45
    sp_std::vec::Vec,
46
    tp_traits::{
47
        ActiveEraInfo, EraIndex, EraIndexProvider, ExternalIndexProvider, InvulnerablesProvider,
48
        OnEraEnd, OnEraStart, ValidatorProvider,
49
    },
50
};
51

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
139
        type OnEraStart: OnEraStart;
140
        type OnEraEnd: OnEraEnd;
141

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
295
7
            <SkipExternalValidators<T>>::put(skip);
296
7

            
297
7
            Ok(())
298
        }
299

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

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

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

            
328
25
            Self::deposit_event(Event::WhitelistedValidatorAdded { account_id: who });
329
25

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

            
337
25
            Ok(Some(weight_used).into())
338
        }
339

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

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

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

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

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

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

            
384
            Self::set_external_validators_inner(validators, external_index)
385
        }
386
    }
387

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

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

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

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

            
416
979
        pub fn active_era() -> Option<ActiveEraInfo> {
417
979
            <ActiveEra<T>>::get()
418
979
        }
419

            
420
796
        pub fn current_era() -> Option<EraIndex> {
421
796
            <CurrentEra<T>>::get()
422
796
        }
423

            
424
1554
        pub fn eras_start_session_index(era: EraIndex) -> Option<u32> {
425
1554
            <ErasStartSessionIndex<T>>::get(era)
426
1554
        }
427

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

            
434
317
            if !SkipExternalValidators::<T>::get() {
435
314
                validators.extend(ExternalValidators::<T>::get())
436
3
            }
437

            
438
317
            remove_duplicates(validators)
439
317
        }
440

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

            
453
577
                let era_length = session_index.saturating_sub(current_era_start_session_index); // Must never happen.
454

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

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

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

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

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

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

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

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

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

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

            
573
307
            // Returns new validators
574
307
            Self::validators()
575
307
        }
576

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

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

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

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

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

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

            
626
1139
    for item in input {
627
822
        if seen.insert(item.clone()) {
628
816
            result.push(item);
629
816
        }
630
    }
631

            
632
317
    result
633
317
}
634

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

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

            
664
497
    fn start_session(start_index: SessionIndex) {
665
497
        <Self as pallet_session::SessionManager<_>>::start_session(start_index)
666
497
    }
667

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

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

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

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

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

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