Browse Source

runtime: Add staking account validation for a member.

Shamil Gadelshin 4 years ago
parent
commit
82c63e1660

+ 6 - 0
runtime-modules/common/src/lib.rs

@@ -42,6 +42,12 @@ pub trait Trait: frame_system::Trait {
         + PartialEq;
 }
 
+/// Validates staking account ownership for a member.
+pub trait StakingAccountValidator<T: Trait> {
+    /// Verifies that staking account bound to the member.
+    fn is_member_staking_account(member_id: &MemberId<T>, account_id: &T::AccountId) -> bool;
+}
+
 /// Defines time in both block number and substrate time abstraction.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Clone, Encode, Decode, PartialEq, Eq, Debug, Default)]

+ 12 - 0
runtime-modules/membership/src/lib.rs

@@ -998,3 +998,15 @@ impl<T: Trait> Module<T> {
         membership_fee.min(referral_cut)
     }
 }
+
+impl<T: Trait> common::StakingAccountValidator<T> for Module<T> {
+    fn is_member_staking_account(
+        member_id: &common::MemberId<T>,
+        account_id: &T::AccountId,
+    ) -> bool {
+        Self::ensure_membership(*member_id)
+            .ok()
+            .map(|membership| membership.staking_account_confirmed(account_id))
+            .unwrap_or(false)
+    }
+}

+ 1 - 0
runtime-modules/membership/src/tests/mock.rs

@@ -111,6 +111,7 @@ impl working_group::Trait<MembershipWorkingGroupInstance> for Test {
     type Event = TestEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type StakingAccountValidator = Membership;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();

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

@@ -233,6 +233,7 @@ impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Test {
     type Event = ();
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = staking_handler::StakingManager<Self, LockId1>;
+    type StakingAccountValidator = membership::Module<Test>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();
@@ -315,6 +316,7 @@ impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = ();
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = staking_handler::StakingManager<Self, LockId2>;
+    type StakingAccountValidator = membership::Module<Test>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();

+ 1 - 0
runtime-modules/service-discovery/src/mock.rs

@@ -146,6 +146,7 @@ impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = staking_handler::StakingManager<Self, LockId1>;
+    type StakingAccountValidator = membership::Module<Test>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();

+ 0 - 9
runtime-modules/staking-handler/src/lib.rs

@@ -6,7 +6,6 @@
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use common::MemberId;
 use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, WithdrawReasons};
 use sp_arithmetic::traits::Zero;
@@ -39,9 +38,6 @@ pub trait StakingHandler<T: frame_system::Trait + common::Trait + pallet_balance
     /// Sets the new stake to a given amount.
     fn set_stake(account_id: &T::AccountId, new_stake: BalanceOf<T>) -> DispatchResult;
 
-    /// Verifies that staking account bound to the member.
-    fn is_member_staking_account(member_id: &MemberId<T>, account_id: &T::AccountId) -> bool;
-
     /// Verifies that there no conflicting stakes on the staking account.
     fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool;
 
@@ -129,11 +125,6 @@ impl<
         Ok(())
     }
 
-    // Membership support for staking accounts required.
-    fn is_member_staking_account(_member_id: &MemberId<T>, _account_id: &T::AccountId) -> bool {
-        true
-    }
-
     fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool {
         let locks = <pallet_balances::Module<T>>::locks(&account_id);
 

+ 0 - 15
runtime-modules/staking-handler/src/test.rs

@@ -177,21 +177,6 @@ fn is_enough_balance_for_stake_succeeds() {
     });
 }
 
-// Test stub: not implemented yet.
-#[ignore]
-#[test]
-fn is_member_staking_account_succeeds() {
-    build_test_externalities().execute_with(|| {
-        let account_id = 1;
-        let member_id = 1;
-
-        assert!(TestStakingManager::is_member_staking_account(
-            &member_id,
-            &account_id
-        ));
-    });
-}
-
 #[test]
 fn set_stake_succeeds() {
     build_test_externalities().execute_with(|| {

+ 1 - 0
runtime-modules/storage/src/tests/mock.rs

@@ -164,6 +164,7 @@ impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type StakingAccountValidator = membership::Module<Test>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();

+ 8 - 2
runtime-modules/working-group/src/lib.rs

@@ -57,7 +57,7 @@ use types::{ApplicationInfo, WorkerInfo};
 pub use checks::{ensure_origin_is_active_leader, ensure_worker_exists, ensure_worker_signed};
 
 use common::origin::ActorOriginValidator;
-use common::MemberId;
+use common::{MemberId, StakingAccountValidator};
 use frame_support::dispatch::DispatchResult;
 use staking_handler::StakingHandler;
 
@@ -104,6 +104,9 @@ pub trait Trait<I: Instance = DefaultInstance>:
     /// Stakes and balance locks handler.
     type StakingHandler: StakingHandler<Self>;
 
+    /// Validates staking account ownership for a member.
+    type StakingAccountValidator: common::StakingAccountValidator<Self>;
+
     /// Validates member id and origin combination
     type MemberOriginValidator: ActorOriginValidator<Self::Origin, MemberId<Self>, Self::AccountId>;
 
@@ -385,7 +388,10 @@ decl_module! {
             // Checks external conditions for staking.
             if let Some(sp) = p.stake_parameters.clone() {
                 ensure!(
-                    T::StakingHandler::is_member_staking_account(&p.member_id, &sp.staking_account_id),
+                    T::StakingAccountValidator::is_member_staking_account(
+                        &p.member_id,
+                        &sp.staking_account_id
+                    ),
                     Error::<T, I>::InvalidStakingAccountForMember
                 );
 

+ 7 - 0
runtime-modules/working-group/src/tests/mock.rs

@@ -119,12 +119,19 @@ impl Trait for Test {
     type Event = TestEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type StakingAccountValidator = ();
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
     type RewardPeriod = RewardPeriod;
     type WeightInfo = ();
 }
 
+impl common::StakingAccountValidator<Test> for () {
+    fn is_member_staking_account(_: &u64, _: &u64) -> bool {
+        true
+    }
+}
+
 impl crate::WeightInfo for () {
     fn on_initialize_leaving(_: u32) -> Weight {
         0

+ 4 - 0
runtime/src/lib.rs

@@ -635,6 +635,7 @@ impl working_group::Trait<ForumWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = ForumWorkingGroupStakingManager;
+    type StakingAccountValidator = Members;
     type MemberOriginValidator = MembershipOriginValidator<Self>;
     type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
     type RewardPeriod = ForumWorkingGroupRewardPeriod;
@@ -645,6 +646,7 @@ impl working_group::Trait<StorageWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = StorageWorkingGroupStakingManager;
+    type StakingAccountValidator = Members;
     type MemberOriginValidator = MembershipOriginValidator<Self>;
     type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
     type RewardPeriod = StorageWorkingGroupRewardPeriod;
@@ -655,6 +657,7 @@ impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = ContentDirectoryWorkingGroupStakingManager;
+    type StakingAccountValidator = Members;
     type MemberOriginValidator = MembershipOriginValidator<Self>;
     type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
     type RewardPeriod = ContentWorkingGroupRewardPeriod;
@@ -665,6 +668,7 @@ impl working_group::Trait<MembershipWorkingGroupInstance> for Runtime {
     type Event = Event;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
     type StakingHandler = MembershipWorkingGroupStakingManager;
+    type StakingAccountValidator = Members;
     type MemberOriginValidator = MembershipOriginValidator<Self>;
     type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
     type RewardPeriod = MembershipRewardPeriod;

+ 16 - 1
runtime/src/tests/proposals_integration/mod.rs

@@ -4,7 +4,7 @@
 
 mod working_group_proposals;
 
-use crate::{BlockNumber, ProposalCancellationFee, Runtime};
+use crate::{BlockNumber, MemberId, ProposalCancellationFee, Runtime};
 use codec::Encode;
 use governance::election_params::ElectionParameters;
 use proposals_codex::{GeneralProposalParameters, ProposalDetails};
@@ -497,6 +497,21 @@ where
     }
 }
 
+pub fn add_confirmed_staking_account(member_id: MemberId, account_id: AccountId32) {
+    assert!(crate::Members::add_staking_account_candidate(
+        RawOrigin::Signed(account_id.clone()).into(),
+        member_id,
+        account_id.clone(),
+    )
+    .is_ok());
+
+    assert!(crate::Members::confirm_staking_account(
+        RawOrigin::Signed(account_id).into(),
+        member_id,
+    )
+    .is_ok());
+}
+
 #[test]
 fn text_proposal_execution_succeeds() {
     initial_test_ext().execute_with(|| {

+ 8 - 0
runtime/src/tests/proposals_integration/working_group_proposals.rs

@@ -535,6 +535,8 @@ fn run_create_decrease_group_leader_stake_proposal_execution_succeeds<
 
         let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
+        add_confirmed_staking_account(member_id, account_id.into());
+
         let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
             RawOrigin::Signed(account_id.into()).into(),
             working_group::ApplyOnOpeningParameters::<T> {
@@ -660,6 +662,8 @@ fn run_create_slash_group_leader_stake_proposal_execution_succeeds<
 
         let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
+        add_confirmed_staking_account(member_id, account_id.into());
+
         let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
             RawOrigin::Signed(account_id.into()).into(),
             working_group::ApplyOnOpeningParameters::<T> {
@@ -950,6 +954,8 @@ fn run_create_terminate_group_leader_role_proposal_execution_succeeds<
 
         let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
+        add_confirmed_staking_account(member_id, account_id.into());
+
         let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
             RawOrigin::Signed(account_id.into()).into(),
             working_group::ApplyOnOpeningParameters::<T> {
@@ -1074,6 +1080,8 @@ fn run_create_terminate_group_leader_role_proposal_with_slashing_execution_succe
 
         let opening_id = add_opening(member_id, account_id, stake_policy, 1, working_group);
 
+        add_confirmed_staking_account(member_id, account_id.into());
+
         let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
             RawOrigin::Signed(account_id.into()).into(),
             working_group::ApplyOnOpeningParameters::<T> {