use rstd::prelude::*; use runtime_primitives::traits::{As, Zero}; use srml_support::{decl_event, decl_module, decl_storage, ensure, StorageValue}; use system; pub use super::election::{self, CouncilElected, Seat, Seats}; pub use crate::currency::{BalanceOf, GovernanceCurrency}; // Hook For announcing that council term has ended pub trait CouncilTermEnded { fn council_term_ended(); } impl CouncilTermEnded for () { fn council_term_ended() {} } impl CouncilTermEnded for (X,) { fn council_term_ended() { X::council_term_ended(); } } pub trait Trait: system::Trait + GovernanceCurrency { type Event: From> + Into<::Event>; type CouncilTermEnded: CouncilTermEnded; } decl_storage! { trait Store for Module as Council { ActiveCouncil get(active_council) config(): Seats>; TermEndsAt get(term_ends_at) config() : T::BlockNumber = T::BlockNumber::sa(1); } } // Event for this module. decl_event!( pub enum Event where ::BlockNumber { CouncilTermEnded(BlockNumber), NewCouncilTermStarted(BlockNumber), } ); impl CouncilElected>, T::BlockNumber> for Module { fn council_elected(seats: Seats>, term: T::BlockNumber) { >::put(seats); let next_term_ends_at = >::block_number() + term; >::put(next_term_ends_at); Self::deposit_event(RawEvent::NewCouncilTermStarted(next_term_ends_at)); } } impl Module { pub fn is_term_ended() -> bool { >::block_number() >= Self::term_ends_at() } pub fn is_councilor(sender: &T::AccountId) -> bool { Self::active_council().iter().any(|c| c.member == *sender) } } decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; fn on_finalize(now: T::BlockNumber) { if now == Self::term_ends_at() { Self::deposit_event(RawEvent::CouncilTermEnded(now)); T::CouncilTermEnded::council_term_ended(); } } // Sudo methods... /// Force set a zero staked council. Stakes in existing council will vanish into thin air! fn set_council(accounts: Vec) { let new_council: Seats> = accounts.into_iter().map(|account| { Seat { member: account, stake: BalanceOf::::zero(), backers: vec![] } }).collect(); >::put(new_council); } /// Adds a zero staked council member fn add_council_member(account: T::AccountId) { ensure!(!Self::is_councilor(&account), "cannot add same account multiple times"); let seat = Seat { member: account, stake: BalanceOf::::zero(), backers: vec![] }; // add member to existing council >::mutate(|council| council.push(seat)); } fn remove_council_member(account_to_remove: T::AccountId) { ensure!(Self::is_councilor(&account_to_remove), "account is not a councilor"); let filtered_council: Seats> = Self::active_council() .into_iter() .filter(|c| c.member != account_to_remove) .collect(); >::put(filtered_council); } /// Set blocknumber when council term will end fn set_term_ends_at(ends_at: T::BlockNumber) { ensure!(ends_at > >::block_number(), "must set future block number"); >::put(ends_at); } } } #[cfg(test)] mod tests { use crate::governance::mock::*; use runtime_io::with_externalities; use srml_support::*; #[test] fn add_council_member_test() { with_externalities(&mut initial_test_ext(), || { assert!(!Council::is_councilor(&1)); assert_ok!(Council::add_council_member(1)); assert!(Council::is_councilor(&1)); assert_ok!(Council::add_council_member(2)); assert!(Council::is_councilor(&1)); assert!(Council::is_councilor(&2)); }); } #[test] fn remove_council_member_test() { with_externalities(&mut initial_test_ext(), || { assert_ok!(Council::add_council_member(1)); assert_ok!(Council::add_council_member(2)); assert_ok!(Council::add_council_member(3)); assert_ok!(Council::remove_council_member(2)); assert!(!Council::is_councilor(&2)); assert!(Council::is_councilor(&1)); assert!(Council::is_councilor(&3)); }); } #[test] fn set_council_test() { with_externalities(&mut initial_test_ext(), || { assert_ok!(Council::set_council(vec![4, 5, 6])); assert!(Council::is_councilor(&4)); assert!(Council::is_councilor(&5)); assert!(Council::is_councilor(&6)); }); } }