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
extern crate alloc;
26

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

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

            
33
pub use pallet::*;
34
use {
35
    alloc::vec::Vec,
36
    frame_support::{pallet_prelude::*, traits::OneSessionHandler},
37
    scale_info::TypeInfo,
38
    sp_runtime::{traits::AtLeast32BitUnsigned, RuntimeAppPublic},
39
};
40

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

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

            
54
        fn on_before_session_ending();
55
    }
56

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

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

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

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

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

            
93
3189
        T::SessionHandler::apply_new_session(changed, session_index, validators, queued);
94
3189
    }
95

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

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

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

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

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

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

            
142
    fn on_disabled(_i: u32) {}
143

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