Browse Source

staking handler - lock comparator

ondratra 4 years ago
parent
commit
e3b84232df

+ 12 - 2
runtime-modules/council/src/mock.rs

@@ -27,6 +27,7 @@ use sp_runtime::{
     traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
+use staking_handler::{BalanceLock, LockComparator, StakingManager};
 use std::cell::RefCell;
 use std::collections::BTreeMap;
 use std::marker::PhantomData;
@@ -75,8 +76,8 @@ impl Trait for Runtime {
     type IdlePeriodDuration = IdlePeriodDuration;
     type MinCandidateStake = MinCandidateStake;
 
-    type CandidacyLock = staking_handler::StakingManager<Self, CandidacyLockId>;
-    type ElectedMemberLock = staking_handler::StakingManager<Self, ElectedMemberLockId>;
+    type CandidacyLock = StakingManager<Self, CandidacyLockId>;
+    type ElectedMemberLock = StakingManager<Self, ElectedMemberLockId>;
 
     type ElectedMemberRewardPerBlock = ElectedMemberRewardPerBlock;
     type ElectedMemberRewardPeriod = ElectedMemberRewardPeriod;
@@ -299,6 +300,15 @@ parameter_types! {
     pub const MaxLocks: u32 = 50;
 }
 
+impl LockComparator<<Runtime as balances::Trait>::Balance> for Runtime {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[BalanceLock<<Runtime as balances::Trait>::Balance>],
+    ) -> bool {
+        true
+    }
+}
+
 /////////////////// Data structures ////////////////////////////////////////////
 
 #[allow(dead_code)]

+ 0 - 5
runtime-modules/council/src/tests.rs

@@ -373,7 +373,6 @@ fn council_announcement_reset_on_not_enough_winners() {
 
 // Test that two consecutive election rounds can be run and expected council members are elected.
 #[test]
-#[ignore] // ignore until `StakeHandler::is_account_free_of_conflicting_stakes()` reimplemented
 fn council_two_consecutive_rounds() {
     let config = default_genesis_config();
 
@@ -499,7 +498,6 @@ fn council_two_consecutive_rounds() {
 
 // Test that repeated candidacy announcement is forbidden.
 #[test]
-#[ignore] // ignore until `StakeHandler::is_account_free_of_conflicting_stakes()` reimplemented
 fn council_cant_candidate_repeatedly() {
     let config = default_genesis_config();
 
@@ -846,7 +844,6 @@ fn council_member_stake_is_locked() {
 
 // Test that council member's stake is automaticly released after next council is elected.
 #[test]
-#[ignore] // ignore until `StakeHandler::is_account_free_of_conflicting_stakes()` reimplemented
 fn council_member_stake_automaticly_unlocked() {
     let config = default_genesis_config();
 
@@ -1039,7 +1036,6 @@ fn council_candidacy_set_note() {
 
 /// Test that candidating in 2nd council cycle after failed candidacy in 1st cycle releases the 1st cycle's stake.
 #[test]
-#[ignore] // ignore until `StakeHandler::is_account_free_of_conflicting_stakes()` reimplemented
 fn council_repeated_candidacy_unstakes() {
     let config = default_genesis_config();
 
@@ -1121,7 +1117,6 @@ fn council_budget_refill_can_be_planned() {
 
 /// Test that rewards for council members are paid.
 #[test]
-#[ignore] // ignore until `StakeHandler::is_account_free_of_conflicting_stakes()` reimplemented
 fn council_rewards_are_paid() {
     let config = default_genesis_config();
 

+ 13 - 3
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -11,6 +11,7 @@ use sp_runtime::{
     Perbill,
 };
 use sp_staking::SessionIndex;
+use staking_handler::{BalanceLock, LockComparator, StakingManager};
 
 use crate::{ProposalDetailsOf, ProposalEncoder, ProposalParameters};
 use proposals_engine::VotersParameters;
@@ -83,7 +84,7 @@ impl proposals_engine::Trait for Test {
     type VoterOriginValidator = ();
     type TotalVotersCounter = MockVotersParameters;
     type ProposalId = u32;
-    type StakingHandler = staking_handler::StakingManager<Test, LockId>;
+    type StakingHandler = StakingManager<Test, LockId>;
     type CancellationFee = CancellationFee;
     type RejectionFee = RejectionFee;
     type TitleMaxLength = TitleMaxLength;
@@ -217,7 +218,7 @@ pub struct WorkingGroupWeightInfo;
 impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Test {
     type Event = ();
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
-    type StakingHandler = staking_handler::StakingManager<Self, LockId1>;
+    type StakingHandler = StakingManager<Self, LockId1>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();
@@ -299,7 +300,7 @@ impl working_group::WeightInfo for WorkingGroupWeightInfo {
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = ();
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
-    type StakingHandler = staking_handler::StakingManager<Self, LockId2>;
+    type StakingHandler = StakingManager<Self, LockId2>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();
@@ -453,6 +454,15 @@ impl pallet_timestamp::Trait for Test {
     type WeightInfo = ();
 }
 
+impl LockComparator<<Test as balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[BalanceLock<<Test as balances::Trait>::Balance>],
+    ) -> bool {
+        true
+    }
+}
+
 pub fn initial_test_ext() -> sp_io::TestExternalities {
     let t = frame_system::GenesisConfig::default()
         .build_storage::<Test>()

+ 11 - 1
runtime-modules/proposals/engine/src/tests/mock/mod.rs

@@ -20,6 +20,7 @@ pub(crate) mod proposals;
 
 use crate::ProposalObserver;
 pub use proposals::*;
+use staking_handler::{BalanceLock, LockComparator, StakingManager};
 
 // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
 #[derive(Clone, PartialEq, Eq, Debug)]
@@ -94,7 +95,7 @@ impl crate::Trait for Test {
     type VoterOriginValidator = ();
     type TotalVotersCounter = ();
     type ProposalId = u32;
-    type StakingHandler = staking_handler::StakingManager<Test, LockId>;
+    type StakingHandler = StakingManager<Test, LockId>;
     type CancellationFee = CancellationFee;
     type RejectionFee = RejectionFee;
     type TitleMaxLength = TitleMaxLength;
@@ -224,6 +225,15 @@ impl minting::Trait for Test {
     type MintId = u64;
 }
 
+impl LockComparator<<Test as balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[BalanceLock<<Test as balances::Trait>::Balance>],
+    ) -> bool {
+        true
+    }
+}
+
 pub fn initial_test_ext() -> sp_io::TestExternalities {
     let t = frame_system::GenesisConfig::default()
         .build_storage::<Test>()

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

@@ -2,6 +2,7 @@
 
 pub use crate::*;
 
+use frame_support::traits::LockIdentifier;
 use frame_support::weights::Weight;
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
 use sp_core::H256;
@@ -10,6 +11,7 @@ use sp_runtime::{
     traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
+use staking_handler::{BalanceLock, LockComparator, StakingManager};
 
 // The storage working group instance alias.
 pub type StorageWorkingGroupInstance = working_group::Instance2;
@@ -128,7 +130,7 @@ pub struct WorkingGroupWeightInfo;
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
-    type StakingHandler = staking_handler::StakingManager<Self, LockId1>;
+    type StakingHandler = StakingManager<Self, LockId1>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();
@@ -222,6 +224,15 @@ impl pallet_timestamp::Trait for Test {
     type WeightInfo = ();
 }
 
+impl LockComparator<<Test as balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[BalanceLock<<Test as balances::Trait>::Balance>],
+    ) -> bool {
+        true
+    }
+}
+
 pub fn initial_test_ext() -> sp_io::TestExternalities {
     let t = frame_system::GenesisConfig::default()
         .build_storage::<Test>()

+ 21 - 5
runtime-modules/staking-handler/src/lib.rs

@@ -11,11 +11,23 @@ use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, Wit
 use sp_arithmetic::traits::Zero;
 use sp_std::marker::PhantomData;
 
+// re-export BalanceLock used in LockComparator so that modules using StakingHandler don't have to depend on pallet_balances
+pub use pallet_balances::BalanceLock;
+
 #[cfg(test)]
 mod mock;
 #[cfg(test)]
 mod test;
 
+/// Trait for (dis)allowing certain stake locks combinations.
+pub trait LockComparator<Balance> {
+    /// Checks if stake lock that is about to be used is conflicting with existing locks.
+    fn are_locks_conflicting(
+        new_lock: &LockIdentifier,
+        existing_locks: &[BalanceLock<Balance>],
+    ) -> bool;
+}
+
 /// Defines abstract staking handler to manage user stakes for different activities
 /// like adding a proposal. Implementation should use built-in LockableCurrency
 /// and LockIdentifier to lock balance consistently with pallet_staking.
@@ -52,7 +64,10 @@ pub trait StakingHandler<AccountId, Balance, MemberId> {
 
 /// Implementation of the StakingHandler.
 pub struct StakingManager<
-    T: frame_system::Trait + membership::Trait + pallet_balances::Trait,
+    T: frame_system::Trait
+        + membership::Trait
+        + pallet_balances::Trait
+        + LockComparator<<T as pallet_balances::Trait>::Balance>,
     LockId: Get<LockIdentifier>,
 > {
     trait_marker: PhantomData<T>,
@@ -60,7 +75,10 @@ pub struct StakingManager<
 }
 
 impl<
-        T: frame_system::Trait + membership::Trait + pallet_balances::Trait,
+        T: frame_system::Trait
+            + membership::Trait
+            + pallet_balances::Trait
+            + LockComparator<<T as pallet_balances::Trait>::Balance>,
         LockId: Get<LockIdentifier>,
     >
     StakingHandler<
@@ -155,9 +173,7 @@ impl<
     ) -> bool {
         let locks = <pallet_balances::Module<T>>::locks(&account_id);
 
-        let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
-
-        existing_lock.is_none()
+        T::are_locks_conflicting(&LockId::get(), locks.as_slice())
     }
 
     fn is_enough_balance_for_stake(

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

@@ -1,3 +1,5 @@
+use crate::{BalanceLock, LockComparator};
+use frame_support::traits::LockIdentifier;
 use frame_support::{impl_outer_origin, parameter_types};
 use frame_system;
 use sp_core::H256;
@@ -74,6 +76,19 @@ impl membership::Trait for Test {
     type MembershipFee = MembershipFee;
 }
 
+impl LockComparator<<Test as pallet_balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        new_lock: &LockIdentifier,
+        existing_locks: &[BalanceLock<<Test as pallet_balances::Trait>::Balance>],
+    ) -> bool {
+        // simple check preventing lock reuse
+        existing_locks
+            .iter()
+            .find(|lock| &lock.id == new_lock)
+            .is_none()
+    }
+}
+
 impl common::currency::GovernanceCurrency for Test {
     type Currency = Balances;
 }

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

@@ -10,6 +10,7 @@ use sp_runtime::{
     traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
+use staking_handler::{BalanceLock, LockComparator, StakingManager};
 
 use crate::data_directory::ContentIdExists;
 use crate::data_object_type_registry::IsActiveDataObjectType;
@@ -160,7 +161,7 @@ pub struct WorkingGroupWeightInfo;
 impl working_group::Trait<StorageWorkingGroupInstance> for Test {
     type Event = MetaEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
-    type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type StakingHandler = StakingManager<Self, LockId>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = ();
     type RewardPeriod = ();
@@ -291,6 +292,15 @@ impl recurringrewards::Trait for Test {
     type RewardRelationshipId = u64;
 }
 
+impl LockComparator<<Test as balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[BalanceLock<<Test as balances::Trait>::Balance>],
+    ) -> bool {
+        true
+    }
+}
+
 pub struct ExtBuilder {
     first_data_object_type_id: u64,
     first_content_id: u64,

+ 12 - 1
runtime-modules/working-group/src/tests/mock.rs

@@ -1,3 +1,4 @@
+use frame_support::traits::LockIdentifier;
 use frame_support::traits::{OnFinalize, OnInitialize};
 use frame_support::weights::Weight;
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
@@ -8,6 +9,7 @@ use sp_runtime::{
     traits::{BlakeTwo256, IdentityLookup},
     Perbill,
 };
+use staking_handler::{BalanceLock, LockComparator, StakingManager};
 
 use crate::{DefaultInstance, Module, Trait};
 
@@ -102,6 +104,15 @@ impl membership::Trait for Test {
     type MembershipFee = MembershipFee;
 }
 
+impl LockComparator<<Test as balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[BalanceLock<<Test as balances::Trait>::Balance>],
+    ) -> bool {
+        true
+    }
+}
+
 pub type Balances = balances::Module<Test>;
 pub type System = frame_system::Module<Test>;
 pub type Membership = membership::Module<Test>;
@@ -116,7 +127,7 @@ parameter_types! {
 impl Trait for Test {
     type Event = TestEvent;
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
-    type StakingHandler = staking_handler::StakingManager<Self, LockId>;
+    type StakingHandler = StakingManager<Self, LockId>;
     type MemberOriginValidator = ();
     type MinUnstakingPeriodLimit = MinUnstakingPeriodLimit;
     type RewardPeriod = RewardPeriod;

+ 37 - 0
runtime/src/constants.rs

@@ -1,4 +1,7 @@
 use crate::{BlockNumber, Moment};
+use frame_support::parameter_types;
+use frame_support::traits::LockIdentifier;
+use sp_std::collections::btree_set::BTreeSet;
 
 /// Constants for Babe.
 
@@ -35,6 +38,40 @@ pub const DAYS: BlockNumber = HOURS * 24;
 // 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks.
 pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
 
+parameter_types! {
+    pub const ProposalsLockId: LockIdentifier = [5; 8];
+    pub const StorageWorkingGroupLockId: LockIdentifier = [6; 8];
+    pub const ContentWorkingGroupLockId: LockIdentifier = [7; 8];
+    pub const ForumGroupLockId: LockIdentifier = [8; 8];
+}
+
+lazy_static! {
+    // pairs of allowed lock combinations
+    pub static ref ALLOWED_LOCK_COMBINATIONS: BTreeSet<(LockIdentifier, LockIdentifier)> = [
+        (ForumGroupLockId::get(), ContentWorkingGroupLockId::get()),
+        (ForumGroupLockId::get(), StorageWorkingGroupLockId::get()),
+        (ForumGroupLockId::get(), ProposalsLockId::get()),
+        (ContentWorkingGroupLockId::get(), ForumGroupLockId::get()),
+        (
+            ContentWorkingGroupLockId::get(),
+            StorageWorkingGroupLockId::get()
+        ),
+        (ContentWorkingGroupLockId::get(), ProposalsLockId::get()),
+        (StorageWorkingGroupLockId::get(), ForumGroupLockId::get()),
+        (
+            StorageWorkingGroupLockId::get(),
+            ContentWorkingGroupLockId::get()
+        ),
+        (StorageWorkingGroupLockId::get(), ProposalsLockId::get()),
+        (ProposalsLockId::get(), ForumGroupLockId::get()),
+        (ProposalsLockId::get(), ContentWorkingGroupLockId::get()),
+        (ProposalsLockId::get(), StorageWorkingGroupLockId::get()),
+    ]
+    .iter()
+    .cloned()
+    .collect();
+}
+
 /// Tests only
 #[cfg(any(feature = "std", test))]
 pub mod currency {

+ 16 - 4
runtime/src/lib.rs

@@ -55,6 +55,7 @@ pub use runtime_api::*;
 use integration::proposals::{CouncilManager, ExtrinsicProposalEncoder, MembershipOriginValidator};
 
 use governance::{council, election};
+use staking_handler::{BalanceLock, LockComparator};
 use storage::data_object_storage_registry;
 
 // Node dependencies
@@ -586,6 +587,21 @@ impl forum::Trait for Runtime {
     }
 }
 
+impl LockComparator<<Runtime as pallet_balances::Trait>::Balance> for Runtime {
+    fn are_locks_conflicting(
+        new_lock: &LockIdentifier,
+        existing_locks: &[BalanceLock<<Runtime as pallet_balances::Trait>::Balance>],
+    ) -> bool {
+        for lock in existing_locks {
+            if !ALLOWED_LOCK_COMBINATIONS.contains(&(*new_lock, lock.id)) {
+                return false;
+            }
+        }
+
+        true
+    }
+}
+
 // The forum working group instance alias.
 pub type ForumWorkingGroupInstance = working_group::Instance1;
 
@@ -601,9 +617,6 @@ parameter_types! {
     pub const ForumWorkingGroupRewardPeriod: u32 = 14400 + 10;
     pub const StorageWorkingGroupRewardPeriod: u32 = 14400 + 20;
     pub const ContentWorkingGroupRewardPeriod: u32 = 14400 + 30;
-    pub const StorageWorkingGroupLockId: LockIdentifier = [6; 8];
-    pub const ContentWorkingGroupLockId: LockIdentifier = [7; 8];
-    pub const ForumGroupLockId: LockIdentifier = [8; 8];
 }
 
 // Staking managers type aliases.
@@ -654,7 +667,6 @@ parameter_types! {
     pub const ProposalTitleMaxLength: u32 = 40;
     pub const ProposalDescriptionMaxLength: u32 = 3000;
     pub const ProposalMaxActiveProposalLimit: u32 = 5;
-    pub const ProposalsLockId: LockIdentifier = [5; 8];
 }
 
 impl proposals_engine::Trait for Runtime {