1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! # Equilibrium Session Manager Pallet
//!
//! Equilibrium's Session Manager Pallet is a Substrate module that manages
//! validation of Equilibrium substrate

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

use eq_utils::eq_ensure;
use frame_support::{
    debug, decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult,
    storage::IterableStorageMap, traits::ValidatorRegistration, Parameter,
};
use pallet_session::SessionManager;
use sp_runtime::traits::{MaybeSerializeDeserialize, Member};
use sp_staking::SessionIndex;
use sp_std::prelude::*;
use system as frame_system;
use system::ensure_root;

mod mock;
mod tests;

/// Substrate pallet configuration trait
pub trait Trait: system::Trait {
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;

    /// Representation of validator id
    type ValidatorId: Member + Parameter + MaybeSerializeDeserialize;
    /// Manages validator registration and unregistration
    type RegistrationChecker: ValidatorRegistration<Self::ValidatorId>;
}

decl_storage! {
    trait Store for Module<T: Trait> as EqSessionManager {
        /// Pallet storage - list of all active validators
        pub Validators get(fn validators): map hasher(blake2_128_concat) T::ValidatorId => bool;
        /// Pallet storage - flag showing that active validators list changed
        /// during a session
        pub IsChanged get(fn is_changed): bool;
    }
    add_extra_genesis {
        config(validators): Vec<T::ValidatorId>;

        build(|config: &GenesisConfig<T>| {
            for &ref validator in config.validators.iter() {
                <Validators<T>>::insert(validator, true);
            }
            IsChanged::put(true);
        });
    }
}

decl_event!(
    pub enum Event<T>
    where
        ValidatorId = <T as Trait>::ValidatorId,
    {
        /// Validator successfully added
        ValidatorAdded(ValidatorId),
        /// Validator successfully removed
        ValidatorRemoved(ValidatorId),
    }
);

decl_error! {
    /// Validator registration errors
    pub enum Error for Module<T: Trait> {
        /// Validator was not added because he is already active
        AlreadyAdded,
        /// Validator was not removed: there is no active validator with this id
        AlreadyRemoved,
        /// Validator was not added because validator is not registered
        NotRegistered,
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        type Error = Error<T>;

        fn deposit_event() = default;

        /// Adds validator. Sudo authorization required to add validator.
        #[weight = 10_000]
        pub fn add_validator(origin, validator_id: T::ValidatorId) -> DispatchResult
        {
            ensure_root(origin)?;

            let is_registered = T::RegistrationChecker::is_registered(&validator_id);
            eq_ensure!(is_registered, Error::<T>::NotRegistered,
            "{}:{}. Validator is not registered. Validator id: {:?}.",
            file!(), line!(), validator_id);

            let validator = <Validators<T>>::get(&validator_id);
            eq_ensure!(!validator, Error::<T>::AlreadyAdded,
            "{}:{}. Validator is already added. Validator id: {:?}.",
            file!(), line!(), validator_id);

            <Validators<T>>::insert(&validator_id, true);

            IsChanged::put(true);

            debug::warn!("Validator {:?} added", validator_id);

            Self::deposit_event(RawEvent::ValidatorAdded(validator_id));

            Ok(())
        }

        /// Removes validator. Sudo authorization required to remove validator.
        #[weight = 10_000]
        pub fn remove_validator(origin, validator_id: T::ValidatorId) -> DispatchResult
        {
            ensure_root(origin)?;

            let validator = <Validators<T>>::get(&validator_id);
            eq_ensure!(validator, Error::<T>::AlreadyRemoved,
            "{}:{}. Validator is already removed. Validator id: {:?}.",
            file!(), line!(), validator_id);

            <Validators<T>>::remove(&validator_id);

            IsChanged::put(true);

            debug::warn!("Validator {:?} removed", validator_id);

            Self::deposit_event(RawEvent::ValidatorRemoved(validator_id));

            Ok(())
        }
    }
}

impl<T: Trait> Module<T> {
    fn commit() {
        IsChanged::put(false);
    }
}

/// Substrate session manager trait
impl<T: Trait> SessionManager<T::ValidatorId> for Module<T> {
    fn new_session(_: SessionIndex) -> Option<Vec<T::ValidatorId>> {
        let result = if IsChanged::get() {
            Some(<Validators<T>>::iter().map(|(k, _v)| k).collect())
        } else {
            None
        };

        Self::commit();

        result
    }
    fn start_session(_: SessionIndex) {}
    fn end_session(_: SessionIndex) {}
}