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
//! A pallet for managing validators on Starlight.
18

            
19
use {sp_staking::SessionIndex, sp_std::vec::Vec};
20

            
21
pub use pallet::*;
22

            
23
type Session<T> = pallet_session::Pallet<T>;
24

            
25
1
#[frame_support::pallet]
26
pub mod pallet {
27
    use {
28
        super::*,
29
        frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin},
30
        frame_system::pallet_prelude::*,
31
    };
32

            
33
    #[pallet::pallet]
34
    #[pallet::without_storage_info]
35
    pub struct Pallet<T>(_);
36

            
37
    /// Configuration for the parachain proposer.
38
    #[pallet::config]
39
    pub trait Config: frame_system::Config + pallet_session::Config {
40
        /// The overreaching event type.
41
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
42

            
43
        /// Privileged origin that can add or remove validators.
44
        type PrivilegedOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
45
    }
46

            
47
    #[pallet::event]
48
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
49
    pub enum Event<T: Config> {
50
        /// New validators were added to the set.
51
        ValidatorsRegistered(Vec<T::ValidatorId>),
52
        /// Validators were removed from the set.
53
        ValidatorsDeregistered(Vec<T::ValidatorId>),
54
    }
55

            
56
    /// Validators that should be retired, because their Parachain was deregistered.
57
    #[pallet::storage]
58
    pub(crate) type ValidatorsToRetire<T: Config> =
59
        StorageValue<_, Vec<T::ValidatorId>, ValueQuery>;
60

            
61
    /// Validators that should be added.
62
    #[pallet::storage]
63
    pub(crate) type ValidatorsToAdd<T: Config> = StorageValue<_, Vec<T::ValidatorId>, ValueQuery>;
64

            
65
    #[pallet::call]
66
    impl<T: Config> Pallet<T> {
67
        /// Add new validators to the set.
68
        ///
69
        /// The new validators will be active from current session + 2.
70
        #[pallet::call_index(0)]
71
        #[pallet::weight({100_000})]
72
        pub fn register_validators(
73
            origin: OriginFor<T>,
74
            validators: Vec<T::ValidatorId>,
75
        ) -> DispatchResult {
76
            T::PrivilegedOrigin::ensure_origin(origin)?;
77

            
78
            validators
79
                .clone()
80
                .into_iter()
81
                .for_each(|v| ValidatorsToAdd::<T>::append(v));
82

            
83
            Self::deposit_event(Event::ValidatorsRegistered(validators));
84
            Ok(())
85
        }
86

            
87
        /// Remove validators from the set.
88
        ///
89
        /// The removed validators will be deactivated from current session + 2.
90
        #[pallet::call_index(1)]
91
        #[pallet::weight({100_000})]
92
        pub fn deregister_validators(
93
            origin: OriginFor<T>,
94
            validators: Vec<T::ValidatorId>,
95
        ) -> DispatchResult {
96
            T::PrivilegedOrigin::ensure_origin(origin)?;
97

            
98
            validators
99
                .clone()
100
                .into_iter()
101
                .for_each(|v| ValidatorsToRetire::<T>::append(v));
102

            
103
            Self::deposit_event(Event::ValidatorsDeregistered(validators));
104
            Ok(())
105
        }
106
    }
107
}
108

            
109
impl<T: Config> pallet_session::SessionManager<T::ValidatorId> for Pallet<T> {
110
2
    fn new_session(new_index: SessionIndex) -> Option<Vec<T::ValidatorId>> {
111
2
        if new_index <= 1 {
112
2
            return None;
113
        }
114

            
115
        let mut validators = Session::<T>::validators();
116

            
117
        ValidatorsToRetire::<T>::take().iter().for_each(|v| {
118
            if let Some(pos) = validators.iter().position(|r| r == v) {
119
                validators.swap_remove(pos);
120
            }
121
        });
122

            
123
        ValidatorsToAdd::<T>::take().into_iter().for_each(|v| {
124
            if !validators.contains(&v) {
125
                validators.push(v);
126
            }
127
        });
128

            
129
        Some(validators)
130
2
    }
131

            
132
    fn end_session(_: SessionIndex) {}
133

            
134
1
    fn start_session(_start_index: SessionIndex) {}
135
}
136

            
137
impl<T: Config> pallet_session::historical::SessionManager<T::ValidatorId, ()> for Pallet<T> {
138
2
    fn new_session(new_index: SessionIndex) -> Option<Vec<(T::ValidatorId, ())>> {
139
2
        <Self as pallet_session::SessionManager<_>>::new_session(new_index)
140
2
            .map(|r| r.into_iter().map(|v| (v, Default::default())).collect())
141
2
    }
142

            
143
1
    fn start_session(start_index: SessionIndex) {
144
1
        <Self as pallet_session::SessionManager<_>>::start_session(start_index)
145
1
    }
146

            
147
    fn end_session(end_index: SessionIndex) {
148
        <Self as pallet_session::SessionManager<_>>::end_session(end_index)
149
    }
150
}