Browse Source

Add ‘set_election_parameters’ extrinsic

- add ‘set_election_parameters’ extrinsic
- add tests
- add check for correct proposal type for other extrinsics
Shamil Gadelshin 5 years ago
parent
commit
d510d90940

+ 5 - 5
runtime-modules/proposals/codex/Cargo.toml

@@ -91,6 +91,11 @@ default_features = false
 package = 'substrate-membership-module'
 path = '../../membership'
 
+[dependencies.governance]
+default_features = false
+package = 'substrate-governance-module'
+path = '../../governance'
+
 [dependencies.proposal_engine]
 default_features = false
 package = 'substrate-proposals-engine-module'
@@ -111,8 +116,3 @@ default_features = false
 git = 'https://github.com/paritytech/substrate.git'
 package = 'sr-io'
 rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
-
-[dev-dependencies.governance]
-default_features = false
-package = 'substrate-governance-module'
-path = '../../governance'

+ 65 - 5
runtime-modules/proposals/codex/src/lib.rs

@@ -4,6 +4,7 @@
 //! Supported extrinsics (proposal type):
 //! - create_text_proposal
 //! - create_runtime_upgrade_proposal
+//! - create_set_election_parameters_proposal
 //!
 //! Proposal implementations of this module:
 //! - execute_text_proposal - prints the proposal to the log
@@ -29,11 +30,16 @@ use srml_support::{decl_error, decl_module, decl_storage, ensure, print};
 use system::{ensure_root, RawOrigin};
 
 use common::origin_validator::ActorOriginValidator;
+use governance::election_params::ElectionParameters;
 use proposal_engine::ProposalParameters;
 
 /// 'Proposals codex' substrate module Trait
 pub trait Trait:
-    system::Trait + proposal_engine::Trait + membership::members::Trait + proposal_discussion::Trait
+    system::Trait
+    + proposal_engine::Trait
+    + membership::members::Trait
+    + proposal_discussion::Trait
+    + governance::election::Trait
 {
     /// Defines max allowed text proposal length.
     type TextProposalMaxLength: Get<u32>;
@@ -54,6 +60,12 @@ use srml_support::traits::{Currency, Get};
 pub type BalanceOf<T> =
     <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
 
+/// Balance alias for GovernanceCurrency from common module. TODO: replace with BalanceOf
+pub type BalanceOfGovernanceCurrency<T> =
+    <<T as common::currency::GovernanceCurrency>::Currency as Currency<
+        <T as system::Trait>::AccountId,
+    >>::Balance;
+
 /// Balance alias for staking
 pub type NegativeImbalance<T> =
     <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
@@ -124,14 +136,14 @@ decl_module! {
         /// Predefined errors
         type Error = Error;
 
-        /// Create text (signal) proposal type. On approval prints its content.
+        /// Create text (signal) proposal type.
         pub fn create_text_proposal(
             origin,
             member_id: MemberId<T>,
             title: Vec<u8>,
             description: Vec<u8>,
-            text: Vec<u8>,
             stake_balance: Option<BalanceOf<T>>,
+            text: Vec<u8>,
         ) {
             let account_id = T::MembershipOriginValidator::ensure_actor_origin(origin, member_id.clone())?;
 
@@ -173,14 +185,14 @@ decl_module! {
              <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
         }
 
-        /// Create runtime upgrade proposal type. On approval prints its content.
+        /// Create runtime upgrade proposal type.
         pub fn create_runtime_upgrade_proposal(
             origin,
             member_id: MemberId<T>,
             title: Vec<u8>,
             description: Vec<u8>,
-            wasm: Vec<u8>,
             stake_balance: Option<BalanceOf<T>>,
+            wasm: Vec<u8>,
         ) {
             let account_id = T::MembershipOriginValidator::ensure_actor_origin(origin, member_id.clone())?;
 
@@ -222,6 +234,54 @@ decl_module! {
             <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
         }
 
+        /// Create 'Set election parameters' proposal type. This proposal uses set_election_parameters()
+        /// extrinsic from the governance::election module.
+        pub fn create_set_election_parameters_proposal(
+            origin,
+            member_id: MemberId<T>,
+            title: Vec<u8>,
+            description: Vec<u8>,
+            stake_balance: Option<BalanceOf<T>>,
+            election_parameters: ElectionParameters<BalanceOfGovernanceCurrency<T>, T::BlockNumber>,
+        ) {
+            let account_id = T::MembershipOriginValidator::ensure_actor_origin(origin, member_id.clone())?;
+
+            let parameters = proposal_types::parameters::set_election_parameters_proposal::<T>();
+
+            <proposal_engine::Module<T>>::ensure_create_proposal_parameters_are_valid(
+                &parameters,
+                &title,
+                &description,
+                stake_balance,
+            )?;
+
+            <proposal_discussion::Module<T>>::ensure_can_create_thread(
+                &title,
+                member_id.clone(),
+            )?;
+
+            election_parameters.ensure_valid()?;
+
+            let proposal_code = <governance::election::Call<T>>::set_election_parameters(election_parameters);
+
+            let discussion_thread_id = <proposal_discussion::Module<T>>::create_thread(
+                member_id,
+                title.clone(),
+            )?;
+
+            let proposal_id = <proposal_engine::Module<T>>::create_proposal(
+                account_id,
+                member_id,
+                parameters,
+                title,
+                description,
+                stake_balance,
+                proposal_code.encode(),
+            )?;
+
+             <ThreadIdByProposalId<T>>::insert(proposal_id, discussion_thread_id);
+        }
+
 // *************** Extrinsic to execute
 
         /// Text proposal extrinsic. Should be used as callable object to pass to the engine module.

+ 14 - 0
runtime-modules/proposals/codex/src/proposal_types/mod.rs

@@ -28,4 +28,18 @@ pub(crate) mod parameters {
             required_stake: Some(<BalanceOf<T>>::from(500u32)),
         }
     }
+
+    // Proposal parameters for the 'Set Election Parameters' proposal
+    pub(crate) fn set_election_parameters_proposal<T: crate::Trait>(
+    ) -> ProposalParameters<T::BlockNumber, BalanceOf<T>> {
+        ProposalParameters {
+            voting_period: T::BlockNumber::from(50000u32),
+            grace_period: T::BlockNumber::from(10000u32),
+            approval_quorum_percentage: 40,
+            approval_threshold_percentage: 51,
+            slashing_quorum_percentage: 80,
+            slashing_threshold_percentage: 80,
+            required_stake: Some(<BalanceOf<T>>::from(500u32)),
+        }
+    }
 }

+ 5 - 0
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -150,6 +150,11 @@ parameter_types! {
     pub const RuntimeUpgradeWasmProposalMaxLength: u32 = 20_000;
 }
 
+impl governance::election::Trait for Test {
+    type Event = ();
+    type CouncilElected = ();
+}
+
 impl crate::Trait for Test {
     type TextProposalMaxLength = TextProposalMaxLength;
     type RuntimeUpgradeWasmProposalMaxLength = RuntimeUpgradeWasmProposalMaxLength;

+ 157 - 12
runtime-modules/proposals/codex/src/tests/mod.rs

@@ -1,5 +1,6 @@
 mod mock;
 
+use governance::election_params::ElectionParameters;
 use srml_support::traits::Currency;
 use srml_support::StorageMap;
 use system::RawOrigin;
@@ -23,8 +24,8 @@ fn create_text_proposal_codex_call_succeeds() {
                 proposer_id,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                b"text".to_vec(),
                 required_stake,
+                b"text".to_vec(),
             ),
             Ok(())
         );
@@ -32,6 +33,14 @@ fn create_text_proposal_codex_call_succeeds() {
         // a discussion was created
         let thread_id = <crate::ThreadIdByProposalId<Test>>::get(1);
         assert_eq!(thread_id, 1);
+
+        let proposal_id = 1;
+        let proposal = ProposalsEngine::proposals(proposal_id);
+        // check for correct proposal parameters
+        assert_eq!(
+            proposal.parameters,
+            crate::proposal_types::parameters::text_proposal::<Test>()
+        );
     });
 }
 
@@ -44,8 +53,8 @@ fn create_text_proposal_codex_call_fails_with_invalid_stake() {
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                b"text".to_vec(),
                 None,
+                b"text".to_vec(),
             ),
             Err(Error::Other("EmptyStake"))
         );
@@ -58,8 +67,8 @@ fn create_text_proposal_codex_call_fails_with_invalid_stake() {
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                b"text".to_vec(),
                 invalid_stake,
+                b"text".to_vec(),
             ),
             Err(Error::Other("StakeDiffersFromRequired"))
         );
@@ -78,8 +87,8 @@ fn create_text_proposal_codex_call_fails_with_incorrect_text_size() {
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                long_text,
                 None,
+                long_text,
             ),
             Err(Error::TextProposalSizeExceeded)
         );
@@ -90,8 +99,8 @@ fn create_text_proposal_codex_call_fails_with_incorrect_text_size() {
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                Vec::new(),
                 None,
+                Vec::new(),
             ),
             Err(Error::TextProposalIsEmpty)
         );
@@ -108,8 +117,8 @@ fn create_text_proposal_codex_call_fails_with_insufficient_rights() {
             1,
             b"title".to_vec(),
             b"body".to_vec(),
-            b"text".to_vec(),
             None,
+            b"text".to_vec(),
         )
         .is_err());
     });
@@ -127,8 +136,8 @@ fn create_upgrade_runtime_proposal_codex_call_fails_with_incorrect_wasm_size() {
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                long_wasm,
                 None,
+                long_wasm,
             ),
             Err(Error::RuntimeProposalSizeExceeded)
         );
@@ -139,8 +148,8 @@ fn create_upgrade_runtime_proposal_codex_call_fails_with_incorrect_wasm_size() {
                 1,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                Vec::new(),
                 None,
+                Vec::new(),
             ),
             Err(Error::RuntimeProposalIsEmpty)
         );
@@ -157,8 +166,8 @@ fn create_upgrade_runtime_proposal_codex_call_fails_with_insufficient_rights() {
             1,
             b"title".to_vec(),
             b"body".to_vec(),
-            b"wasm".to_vec(),
             None,
+            b"wasm".to_vec(),
         )
         .is_err());
     });
@@ -174,8 +183,8 @@ fn create_runtime_upgrade_proposal_codex_call_fails_with_invalid_stake() {
                 proposer_id,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                b"wasm".to_vec(),
                 None,
+                b"wasm".to_vec(),
             ),
             Err(Error::Other("EmptyStake"))
         );
@@ -188,8 +197,8 @@ fn create_runtime_upgrade_proposal_codex_call_fails_with_invalid_stake() {
                 proposer_id,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                b"wasm".to_vec(),
                 invalid_stake,
+                b"wasm".to_vec(),
             ),
             Err(Error::Other("StakeDiffersFromRequired"))
         );
@@ -212,8 +221,8 @@ fn create_runtime_upgrade_proposal_codex_call_succeeds() {
                 proposer_id,
                 b"title".to_vec(),
                 b"body".to_vec(),
-                b"wasm".to_vec(),
                 required_stake,
+                b"wasm".to_vec(),
             ),
             Ok(())
         );
@@ -221,5 +230,141 @@ fn create_runtime_upgrade_proposal_codex_call_succeeds() {
         // a discussion was created
         let thread_id = <crate::ThreadIdByProposalId<Test>>::get(1);
         assert_eq!(thread_id, 1);
+
+        let proposal_id = 1;
+        let proposal = ProposalsEngine::proposals(proposal_id);
+        // check for correct proposal parameters
+        assert_eq!(
+            proposal.parameters,
+            crate::proposal_types::parameters::upgrade_runtime::<Test>()
+        );
+    });
+}
+
+#[test]
+fn create_set_election_parameters_call_fails_with_insufficient_rights() {
+    initial_test_ext().execute_with(|| {
+        let origin = RawOrigin::None.into();
+
+        assert!(ProposalCodex::create_set_election_parameters_proposal(
+            origin,
+            1,
+            b"title".to_vec(),
+            b"body".to_vec(),
+            None,
+            ElectionParameters::default(),
+        )
+        .is_err());
+    });
+}
+
+#[test]
+fn create_set_election_parameters_call_fails_with_incorrect_parameters() {
+    initial_test_ext().execute_with(|| {
+        let account_id = 1;
+        let origin = RawOrigin::Signed(account_id).into();
+
+        let required_stake = Some(<BalanceOf<Test>>::from(500u32));
+        let _imbalance = <Test as stake::Trait>::Currency::deposit_creating(&account_id, 50000);
+
+        assert_eq!(
+            ProposalCodex::create_set_election_parameters_proposal(
+                origin,
+                1,
+                b"title".to_vec(),
+                b"body".to_vec(),
+                required_stake,
+                ElectionParameters::default(),
+            ),
+            Err(Error::Other("PeriodCannotBeZero"))
+        );
+    });
+}
+
+#[test]
+fn create_set_election_parameters_call_fails_with_invalid_stake() {
+    initial_test_ext().execute_with(|| {
+        let origin = RawOrigin::Signed(1).into();
+
+        let election_parameters = ElectionParameters {
+            announcing_period: 1,
+            voting_period: 2,
+            revealing_period: 3,
+            council_size: 4,
+            candidacy_limit: 5,
+            min_voting_stake: 6,
+            min_council_stake: 7,
+            new_term_duration: 8,
+        };
+
+        assert_eq!(
+            ProposalCodex::create_set_election_parameters_proposal(
+                origin,
+                1,
+                b"title".to_vec(),
+                b"body".to_vec(),
+                None,
+                election_parameters.clone(),
+            ),
+            Err(Error::Other("EmptyStake"))
+        );
+
+        let invalid_stake = Some(<BalanceOf<Test>>::from(5000u32));
+
+        assert_eq!(
+            ProposalCodex::create_set_election_parameters_proposal(
+                RawOrigin::Signed(1).into(),
+                1,
+                b"title".to_vec(),
+                b"body".to_vec(),
+                invalid_stake,
+                election_parameters,
+            ),
+            Err(Error::Other("StakeDiffersFromRequired"))
+        );
+    });
+}
+
+#[test]
+fn create_set_election_parameters_call_succeeds() {
+    initial_test_ext().execute_with(|| {
+        let account_id = 1;
+        let origin = RawOrigin::Signed(account_id).into();
+
+        let required_stake = Some(<BalanceOf<Test>>::from(500u32));
+        let _imbalance = <Test as stake::Trait>::Currency::deposit_creating(&account_id, 50000);
+
+        let election_parameters = ElectionParameters {
+            announcing_period: 1,
+            voting_period: 2,
+            revealing_period: 3,
+            council_size: 4,
+            candidacy_limit: 5,
+            min_voting_stake: 6,
+            min_council_stake: 7,
+            new_term_duration: 8,
+        };
+
+        assert!(ProposalCodex::create_set_election_parameters_proposal(
+            origin,
+            1,
+            b"title".to_vec(),
+            b"body".to_vec(),
+            required_stake,
+            election_parameters,
+        )
+        .is_ok());
+
+        // a discussion was created
+        let thread_id = <crate::ThreadIdByProposalId<Test>>::get(1);
+        assert_eq!(thread_id, 1);
+
+        let proposal_id = 1;
+        let proposal = ProposalsEngine::proposals(proposal_id);
+        // check for correct proposal parameters
+        assert_eq!(
+            proposal.parameters,
+            crate::proposal_types::parameters::set_election_parameters_proposal::<Test>()
+        );
     });
 }