Kaynağa Gözat

Merge branch 'development' into constantinople-release

Mokhtar Naamani 4 yıl önce
ebeveyn
işleme
8b6aca619f

+ 1 - 0
Cargo.lock

@@ -4837,6 +4837,7 @@ dependencies = [
  "substrate-common-module",
  "substrate-membership-module",
  "substrate-primitives",
+ "substrate-recurring-reward-module",
  "substrate-token-mint-module",
 ]
 

+ 6 - 1
runtime-modules/governance/Cargo.toml

@@ -92,4 +92,9 @@ rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 [dependencies.minting]
 default_features = false
 package = 'substrate-token-mint-module'
-path = '../token-minting'
+path = '../token-minting'
+
+[dependencies.recurringrewards]
+default_features = false
+package = 'substrate-recurring-reward-module'
+path = '../recurring-reward'

+ 169 - 9
runtime-modules/governance/src/council.rs

@@ -1,6 +1,6 @@
 use rstd::prelude::*;
-use sr_primitives::traits::Zero;
-use srml_support::{decl_event, decl_module, decl_storage, ensure};
+use sr_primitives::traits::{One, Zero};
+use srml_support::{debug, decl_event, decl_module, decl_storage, ensure};
 use system::{self, ensure_root};
 
 pub use super::election::{self, CouncilElected, Seat, Seats};
@@ -21,7 +21,7 @@ impl<X: CouncilTermEnded> CouncilTermEnded for (X,) {
     }
 }
 
-pub trait Trait: system::Trait + minting::Trait + GovernanceCurrency {
+pub trait Trait: system::Trait + recurringrewards::Trait + GovernanceCurrency {
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 
     type CouncilTermEnded: CouncilTermEnded;
@@ -37,6 +37,20 @@ decl_storage! {
         /// because it was introduced in a runtime upgrade. It will be automatically created when
         /// a successful call to set_council_mint_capacity() is made.
         pub CouncilMint get(council_mint) : Option<<T as minting::Trait>::MintId>;
+
+        /// The reward relationships currently in place. There may not necessarily be a 1-1 correspondance with
+        /// the active council, since there are multiple ways of setting/adding/removing council members, some of which
+        /// do not involve creating a relationship.
+        pub RewardRelationships get(reward_relationships) : map T::AccountId => T::RewardRelationshipId;
+
+        /// Reward amount paid out at each PayoutInterval
+        pub AmountPerPayout get(amount_per_payout): minting::BalanceOf<T>;
+
+        /// Optional interval in blocks on which a reward payout will be made to each council member
+        pub PayoutInterval get(payout_interval): Option<T::BlockNumber>;
+
+        /// How many blocks after the reward is created, the first payout will be made
+        pub FirstPayoutAfterRewardCreated get(first_payout_after_reward_created): T::BlockNumber;
     }
 }
 
@@ -50,10 +64,23 @@ decl_event!(
 
 impl<T: Trait> CouncilElected<Seats<T::AccountId, BalanceOf<T>>, T::BlockNumber> for Module<T> {
     fn council_elected(seats: Seats<T::AccountId, BalanceOf<T>>, term: T::BlockNumber) {
-        <ActiveCouncil<T>>::put(seats);
+        <ActiveCouncil<T>>::put(seats.clone());
 
         let next_term_ends_at = <system::Module<T>>::block_number() + term;
+
         <TermEndsAt<T>>::put(next_term_ends_at);
+
+        if let Some(reward_source) = Self::council_mint() {
+            for seat in seats.iter() {
+                Self::add_reward_relationship(&seat.member, reward_source);
+            }
+        } else {
+            // Skip trying to create rewards since no mint has been created yet
+            debug::warn!(
+                "Not creating reward relationship for council seats because no mint exists"
+            );
+        }
+
         Self::deposit_event(RawEvent::NewCouncilTermStarted(next_term_ends_at));
     }
 }
@@ -75,6 +102,51 @@ impl<T: Trait> Module<T> {
         CouncilMint::<T>::put(mint_id);
         Ok(mint_id)
     }
+
+    fn add_reward_relationship(destination: &T::AccountId, reward_source: T::MintId) {
+        let recipient = <recurringrewards::Module<T>>::add_recipient();
+
+        // When calculating when first payout occurs, add minimum of one block interval to ensure rewards module
+        // has a chance to execute its on_finalize routine.
+        let next_payout_at = system::Module::<T>::block_number()
+            + Self::first_payout_after_reward_created()
+            + T::BlockNumber::one();
+
+        if let Ok(relationship_id) = <recurringrewards::Module<T>>::add_reward_relationship(
+            reward_source,
+            recipient,
+            destination.clone(),
+            Self::amount_per_payout(),
+            next_payout_at,
+            Self::payout_interval(),
+        ) {
+            RewardRelationships::<T>::insert(destination, relationship_id);
+        } else {
+            debug::warn!("Failed to create a reward relationship for council seat");
+        }
+    }
+
+    fn remove_reward_relationships() {
+        for seat in Self::active_council().into_iter() {
+            if RewardRelationships::<T>::exists(&seat.member) {
+                let id = Self::reward_relationships(&seat.member);
+                <recurringrewards::Module<T>>::remove_reward_relationship(id);
+            }
+        }
+    }
+
+    fn on_term_ended(now: T::BlockNumber) {
+        // Stop paying out rewards when the term ends.
+        // Note: Is it not simpler to just do a single payout at end of term?
+        // During the term the recurring reward module could unfairly pay some but not all council members
+        // If there is insufficient mint capacity.. so doing it at this point offers more control
+        // and a potentially more fair outcome in such a case.
+        Self::remove_reward_relationships();
+
+        Self::deposit_event(RawEvent::CouncilTermEnded(now));
+
+        T::CouncilTermEnded::council_term_ended();
+    }
 }
 
 decl_module! {
@@ -83,16 +155,28 @@ decl_module! {
 
         fn on_finalize(now: T::BlockNumber) {
             if now == Self::term_ends_at() {
-                Self::deposit_event(RawEvent::CouncilTermEnded(now));
-                T::CouncilTermEnded::council_term_ended();
+                Self::on_term_ended(now);
             }
         }
 
         // Privileged methods
 
-        /// Force set a zero staked council. Stakes in existing council will vanish into thin air!
+        /// Force set a zero staked council. Stakes in existing council seats are not returned.
+        /// Existing council rewards are removed and new council members do NOT get any rewards.
+        /// Avoid using this call if possible, will be deprecated. The term of the new council is
+        /// not extended.
         pub fn set_council(origin, accounts: Vec<T::AccountId>) {
             ensure_root(origin)?;
+
+            // Council is being replaced so remove existing reward relationships if they exist
+            Self::remove_reward_relationships();
+
+            if let Some(reward_source) = Self::council_mint() {
+                for account in accounts.clone() {
+                    Self::add_reward_relationship(&account, reward_source);
+                }
+            }
+
             let new_council: Seats<T::AccountId, BalanceOf<T>> = accounts.into_iter().map(|account| {
                 Seat {
                     member: account,
@@ -100,13 +184,20 @@ decl_module! {
                     backers: vec![]
                 }
             }).collect();
+
             <ActiveCouncil<T>>::put(new_council);
         }
 
-        /// Adds a zero staked council member
+        /// Adds a zero staked council member. A member added in this way does not get a recurring reward.
         fn add_council_member(origin, account: T::AccountId) {
             ensure_root(origin)?;
+
             ensure!(!Self::is_councilor(&account), "cannot add same account multiple times");
+
+            if let Some(reward_source) = Self::council_mint() {
+                Self::add_reward_relationship(&account, reward_source);
+            }
+
             let seat = Seat {
                 member: account,
                 stake: BalanceOf::<T>::zero(),
@@ -117,13 +208,22 @@ decl_module! {
             <ActiveCouncil<T>>::mutate(|council| council.push(seat));
         }
 
+        /// Remove a single council member and their reward.
         fn remove_council_member(origin, account_to_remove: T::AccountId) {
             ensure_root(origin)?;
+
             ensure!(Self::is_councilor(&account_to_remove), "account is not a councilor");
+
+            if RewardRelationships::<T>::exists(&account_to_remove) {
+                let relationship_id = Self::reward_relationships(&account_to_remove);
+                <recurringrewards::Module<T>>::remove_reward_relationship(relationship_id);
+            }
+
             let filtered_council: Seats<T::AccountId, BalanceOf<T>> = Self::active_council()
                 .into_iter()
                 .filter(|c| c.member != account_to_remove)
                 .collect();
+
             <ActiveCouncil<T>>::put(filtered_council);
         }
 
@@ -153,7 +253,26 @@ decl_module! {
             if let Some(mint_id) = Self::council_mint() {
                 minting::Module::<T>::transfer_tokens(mint_id, amount, &destination)?;
             } else {
-                return Err("CouncilHashNoMint")
+                return Err("CouncilHasNoMint")
+            }
+        }
+
+        /// Sets the council rewards which is only applied on new council being elected.
+        fn set_council_rewards(
+            origin,
+            amount_per_payout: minting::BalanceOf<T>,
+            payout_interval: Option<T::BlockNumber>,
+            first_payout_after_reward_created: T::BlockNumber
+        ) {
+            ensure_root(origin)?;
+
+            AmountPerPayout::<T>::put(amount_per_payout);
+            FirstPayoutAfterRewardCreated::<T>::put(first_payout_after_reward_created);
+
+            if let Some(payout_interval) = payout_interval {
+                PayoutInterval::<T>::put(payout_interval);
+            } else {
+                PayoutInterval::<T>::take();
             }
         }
     }
@@ -161,6 +280,7 @@ decl_module! {
 
 #[cfg(test)]
 mod tests {
+    use super::*;
     use crate::mock::*;
     use srml_support::*;
 
@@ -212,4 +332,44 @@ mod tests {
             assert!(Council::is_councilor(&6));
         });
     }
+
+    #[test]
+    fn council_elected_test() {
+        initial_test_ext().execute_with(|| {
+            // Ensure a mint is created so we can create rewards
+            assert_ok!(Council::set_council_mint_capacity(
+                system::RawOrigin::Root.into(),
+                1000
+            ));
+
+            Council::council_elected(
+                vec![
+                    Seat {
+                        member: 5,
+                        stake: 0,
+                        backers: vec![],
+                    },
+                    Seat {
+                        member: 6,
+                        stake: 0,
+                        backers: vec![],
+                    },
+                    Seat {
+                        member: 7,
+                        stake: 0,
+                        backers: vec![],
+                    },
+                ],
+                50 as u64, // <Test as system::Trait>::BlockNumber::from(50)
+            );
+
+            assert!(Council::is_councilor(&5));
+            assert!(Council::is_councilor(&6));
+            assert!(Council::is_councilor(&7));
+
+            assert!(RewardRelationships::<Test>::exists(&5));
+            assert!(RewardRelationships::<Test>::exists(&6));
+            assert!(RewardRelationships::<Test>::exists(&7));
+        });
+    }
 }

+ 5 - 0
runtime-modules/governance/src/mock.rs

@@ -74,6 +74,11 @@ impl minting::Trait for Test {
     type Currency = Balances;
     type MintId = u64;
 }
+impl recurringrewards::Trait for Test {
+    type PayoutStatusHandler = ();
+    type RecipientId = u64;
+    type RewardRelationshipId = u64;
+}
 parameter_types! {
     pub const ExistentialDeposit: u32 = 0;
     pub const TransferFee: u32 = 0;

+ 0 - 5
runtime/src/migration.rs

@@ -33,11 +33,6 @@ impl<T: Trait> Module<T> {
 
         // Initialise the proposal system various periods
         proposals_codex::Module::<T>::set_default_config_values();
-
-        Self::deposit_event(RawEvent::Migrated(
-            <system::Module<T>>::block_number(),
-            VERSION.spec_version,
-        ));
     }
 }