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
//! # Initializer Pallet
18
//!
19
//! This pallet is in charge of organizing what happens on session changes.
20
//! In particular this pallet has implemented the OneSessionHandler trait
21
//! which will be called upon a session change. There it will call the
22
//! SessionHandler config trait
23

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

            
26
#[cfg(test)]
27
mod mock;
28

            
29
#[cfg(test)]
30
mod tests;
31

            
32
pub use pallet::*;
33
use {
34
    frame_support::{pallet_prelude::*, traits::OneSessionHandler},
35
    scale_info::TypeInfo,
36
    sp_runtime::{traits::AtLeast32BitUnsigned, RuntimeAppPublic},
37
    sp_std::prelude::*,
38
};
39

            
40
2472
#[frame_support::pallet]
41
pub mod pallet {
42
    use super::*;
43

            
44
    // The apply_new_session trait. We need to comply with this
45
    pub trait ApplyNewSession<T: Config> {
46
        fn apply_new_session(
47
            changed: bool,
48
            session_index: T::SessionIndex,
49
            all_validators: Vec<(T::AccountId, T::AuthorityId)>,
50
            queued: Vec<(T::AccountId, T::AuthorityId)>,
51
        );
52

            
53
        fn on_before_session_ending();
54
    }
55

            
56
68755
    #[pallet::pallet]
57
    pub struct Pallet<T>(_);
58

            
59
    #[pallet::config]
60
    pub trait Config: frame_system::Config {
61
        type SessionIndex: parity_scale_codec::FullCodec + TypeInfo + Copy + AtLeast32BitUnsigned;
62

            
63
        /// The identifier type for an authority.
64
        type AuthorityId: Member
65
            + Parameter
66
            + RuntimeAppPublic
67
            + MaybeSerializeDeserialize
68
            + MaxEncodedLen;
69

            
70
        type SessionHandler: ApplyNewSession<Self>;
71
    }
72
}
73

            
74
impl<T: Config> Pallet<T> {
75
    /// Should be called when a new session occurs. If `queued` is `None`,
76
    /// the `validators` are considered queued.
77
4541
    fn on_new_session<'a, I>(
78
4541
        changed: bool,
79
4541
        session_index: T::SessionIndex,
80
4541
        validators: I,
81
4541
        queued: Option<I>,
82
4541
    ) where
83
4541
        I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)> + 'a,
84
4541
    {
85
14532
        let validators: Vec<_> = validators.map(|(k, v)| (k.clone(), v)).collect();
86
4541
        let queued: Vec<_> = if let Some(queued) = queued {
87
12080
            queued.map(|(k, v)| (k.clone(), v)).collect()
88
        } else {
89
1301
            validators.clone()
90
        };
91

            
92
4541
        T::SessionHandler::apply_new_session(changed, session_index, validators, queued);
93
4541
    }
94

            
95
    /// Should be called when a new session occurs. Buffers the session notification to be applied
96
    /// at the end of the block. If `queued` is `None`, the `validators` are considered queued.
97
1301
    fn on_genesis_session<'a, I>(validators: I)
98
1301
    where
99
1301
        I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)> + 'a,
100
1301
    {
101
1301
        <Pallet<T>>::on_new_session(false, 0u32.into(), validators, None);
102
1301
    }
103

            
104
    // Allow to trigger `on_new_session` in tests, this is needed as long as `pallet_session` is not
105
    // implemented in mock.
106
    #[cfg(test)]
107
2
    pub(crate) fn test_trigger_on_new_session<'a, I>(
108
2
        changed: bool,
109
2
        session_index: T::SessionIndex,
110
2
        validators: I,
111
2
        queued: Option<I>,
112
2
    ) where
113
2
        I: Iterator<Item = (&'a T::AccountId, T::AuthorityId)> + 'a,
114
2
    {
115
2
        Self::on_new_session(changed, session_index, validators, queued)
116
2
    }
117
}
118

            
119
impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
120
    type Public = T::AuthorityId;
121
}
122

            
123
impl<T: pallet_session::Config + Config> OneSessionHandler<T::AccountId> for Pallet<T> {
124
    type Key = T::AuthorityId;
125

            
126
1301
    fn on_genesis_session<'a, I>(validators: I)
127
1301
    where
128
1301
        I: Iterator<Item = (&'a T::AccountId, Self::Key)> + 'a,
129
1301
    {
130
1301
        <Pallet<T>>::on_genesis_session(validators);
131
1301
    }
132

            
133
3238
    fn on_new_session<'a, I>(changed: bool, validators: I, queued: I)
134
3238
    where
135
3238
        I: Iterator<Item = (&'a T::AccountId, Self::Key)> + 'a,
136
3238
    {
137
3238
        let session_index = <pallet_session::Pallet<T>>::current_index();
138
3238
        <Pallet<T>>::on_new_session(changed, session_index.into(), validators, Some(queued));
139
3238
    }
140

            
141
    fn on_disabled(_i: u32) {}
142

            
143
3238
    fn on_before_session_ending() {
144
3238
        <T as Config>::SessionHandler::on_before_session_ending();
145
3238
    }
146
}