Explorar el Código

council - StakingHandler integration

ondratra hace 4 años
padre
commit
6d88838076

+ 3 - 0
Cargo.lock

@@ -3342,6 +3342,9 @@ dependencies = [
  "frame-support",
  "frame-system",
  "pallet-balances",
+ "pallet-common",
+ "pallet-membership",
+ "pallet-timestamp",
  "parity-scale-codec",
  "rand 0.7.3",
  "serde",

+ 12 - 1
runtime-modules/council/Cargo.toml

@@ -14,8 +14,15 @@ frame-support = { package = 'frame-support', default-features = false, git = 'ht
 system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 referendum = { package = 'substrate-referendum-module', default-features = false, path = '../referendum'}
 
+# remove this after `staking_handler.rs` is removed in favor of external crate
+membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
+common = { package = 'pallet-common', default-features = false, path = '../common'}
+timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+
 [dev-dependencies]
-pallet-balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+# uncomment balances after `staking_handler.rs` is removed in favor of external crate
+#balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 rand = "0.7.3"
 
@@ -31,5 +38,9 @@ std = [
     'frame-support/std',
     'referendum/std',
     'system/std',
+
+    # remove this after `staking_handler.rs` is removed in favor of external crate
+    'membership/std',
+    'common/std',
 ]
 

+ 12 - 33
runtime-modules/council/src/lib.rs

@@ -38,7 +38,7 @@
 
 // used dependencies
 use codec::{Codec, Decode, Encode};
-use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, WithdrawReason};
+use frame_support::traits::{Currency, Get};
 use frame_support::{
     decl_error, decl_event, decl_module, decl_storage, ensure, error::BadOrigin, Parameter,
 };
@@ -57,6 +57,9 @@ use referendum::{OptionResult, ReferendumManager};
 // declared modules
 mod mock;
 mod tests;
+mod staking_handler;
+
+use staking_handler::{StakingHandler2};
 
 /////////////////// Data Structures ////////////////////////////////////////////
 
@@ -131,11 +134,6 @@ impl<AccountId, CouncilUserId, Balance> From<(Candidate<AccountId, Balance>, Cou
 
 /////////////////// Type aliases ///////////////////////////////////////////////
 
-pub type CurrencyOf<T> = <<T as Trait>::Referendum as ReferendumManager<
-    <T as system::Trait>::Origin,
-    <T as system::Trait>::AccountId,
-    <T as system::Trait>::Hash,
->>::Currency;
 pub type Balance<T> = <<<T as Trait>::Referendum as ReferendumManager<
     <T as system::Trait>::Origin,
     <T as system::Trait>::AccountId,
@@ -182,9 +180,9 @@ pub trait Trait: system::Trait {
     type MinCandidateStake: Get<Balance<Self>>;
 
     /// Identifier for currency lock used for candidacy staking.
-    type CandidacyLockId: Get<LockIdentifier>;
+    type CandidacyLock: StakingHandler2<Self::AccountId, Balance<Self>, Self::CouncilUserId>;
     /// Identifier for currency lock used for candidacy staking.
-    type ElectedMemberLockId: Get<LockIdentifier>;
+    type ElectedMemberLock: StakingHandler2<Self::AccountId, Balance<Self>, Self::CouncilUserId>;
 
     /// Duration of annoncing period
     type AnnouncingPeriodDuration: Get<Self::BlockNumber>;
@@ -544,12 +542,7 @@ impl<T: Trait> Mutations<T> {
         CouncilMembers::<T>::get()
             .iter()
             .for_each(|council_member| {
-                CurrencyOf::<T>::set_lock(
-                    <T as Trait>::CandidacyLockId::get(),
-                    &council_member.account_id,
-                    0.into(),
-                    WithdrawReason::Transfer.into(),
-                )
+                T::CandidacyLock::set_stake(&council_member.account_id, 0.into());
             });
 
         // set new council
@@ -560,21 +553,12 @@ impl<T: Trait> Mutations<T> {
             .iter()
             .for_each(|council_member| {
                 // unlock candidacy stake
-                CurrencyOf::<T>::set_lock(
-                    <T as Trait>::CandidacyLockId::get(),
-                    &council_member.account_id,
-                    0.into(),
-                    WithdrawReason::Transfer.into(),
-                );
+                T::CandidacyLock::set_stake(&council_member.account_id, 0.into());
 
                 // lock council member stake
-                CurrencyOf::<T>::set_lock(
-                    <T as Trait>::ElectedMemberLockId::get(),
-                    &council_member.account_id,
-                    council_member.stake,
-                    WithdrawReason::Transfer.into(),
-                );
+                T::ElectedMemberLock::set_stake(&council_member.account_id, council_member.stake);
             });
+
     }
 
     /// Announce user's candidacy.
@@ -598,12 +582,7 @@ impl<T: Trait> Mutations<T> {
         };
 
         // lock candidacy stake
-        CurrencyOf::<T>::set_lock(
-            <T as Trait>::CandidacyLockId::get(),
-            &candidate.account_id,
-            *stake,
-            WithdrawReason::Transfer.into(),
-        );
+        T::CandidacyLock::set_stake(&candidate.account_id, stake.clone());
 
         // store new candidacy list
         Stage::<T>::mutate(|value| {
@@ -617,7 +596,7 @@ impl<T: Trait> Mutations<T> {
     /// Release user's stake that was used for candidacy.
     fn release_candidacy_stake(account_id: &T::AccountId, council_user_id: &T::CouncilUserId) {
         // release stake amount
-        CurrencyOf::<T>::remove_lock(<T as Trait>::CandidacyLockId::get(), account_id);
+        T::CandidacyLock::set_stake(&account_id, 0.into());
 
         let candidate = Candidates::<T>::get(council_user_id);
 

+ 13 - 11
runtime-modules/council/src/mock.rs

@@ -9,7 +9,7 @@ use crate::{
 
 use frame_support::traits::{Currency, Get, LockIdentifier, OnFinalize};
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, StorageValue};
-use pallet_balances;
+use balances;
 use rand::Rng;
 use referendum::{
     Balance, CastVote, CurrentCycleId, OptionResult, ReferendumManager, ReferendumStage,
@@ -27,6 +27,8 @@ use std::collections::BTreeMap;
 use std::marker::PhantomData;
 use system::{EnsureOneOf, EnsureRoot, EnsureSigned, RawOrigin};
 
+use crate::staking_handler::mocks::{Lock1, Lock2};
+
 pub const USER_REGULAR_POWER_VOTES: u64 = 0;
 
 pub const POWER_VOTE_STRENGTH: u64 = 10;
@@ -57,15 +59,16 @@ impl Trait for Runtime {
 
     type Referendum = referendum::Module<RuntimeReferendum, ReferendumInstance>;
 
-    type CouncilUserId = u64;
-    type CandidacyLockId = CandidacyLockId;
-    type ElectedMemberLockId = ElectedMemberLockId;
+    type CouncilUserId = <Lock1 as membership::Trait>::MemberId;
     type MinNumberOfExtraCandidates = MinNumberOfExtraCandidates;
     type CouncilSize = CouncilSize;
     type AnnouncingPeriodDuration = AnnouncingPeriodDuration;
     type IdlePeriodDuration = IdlePeriodDuration;
     type MinCandidateStake = MinCandidateStake;
 
+    type CandidacyLock = Lock1;
+    type ElectedMemberLock = Lock2;
+
     fn is_council_user(
         account_id: &<Self as system::Trait>::AccountId,
         council_user_id: &Self::CouncilUserId,
@@ -104,7 +107,6 @@ parameter_types! {
     pub const MaximumBlockWeight: u32 = 1024;
     pub const MaximumBlockLength: u32 = 2 * 1024;
     pub const AvailableBlockRatio: Perbill = Perbill::one();
-    pub const MinimumPeriod: u64 = 5;
 }
 
 impl system::Trait for Runtime {
@@ -129,7 +131,7 @@ impl system::Trait for Runtime {
     type AvailableBlockRatio = AvailableBlockRatio;
     type Version = ();
     type ModuleToIndex = ();
-    type AccountData = pallet_balances::AccountData<u64>;
+    type AccountData = balances::AccountData<u64>;
     type OnNewAccount = ();
     type OnKilledAccount = ();
 }
@@ -155,7 +157,7 @@ parameter_types! {
 }
 
 mod balances_mod {
-    pub use pallet_balances::Event;
+    pub use balances::Event;
 }
 
 impl_outer_event! {
@@ -174,7 +176,7 @@ impl referendum::Trait<ReferendumInstance> for RuntimeReferendum {
 
     type MaxSaltLength = MaxSaltLength;
 
-    type Currency = pallet_balances::Module<Self>;
+    type Currency = balances::Module<Self>;
     type LockId = ReferendumLockId;
 
     type ManagerOrigin =
@@ -272,12 +274,12 @@ impl system::Trait for RuntimeReferendum {
     type AvailableBlockRatio = AvailableBlockRatio;
     type Version = ();
     type ModuleToIndex = ();
-    type AccountData = pallet_balances::AccountData<u64>;
+    type AccountData = balances::AccountData<u64>;
     type OnNewAccount = ();
     type OnKilledAccount = ();
 }
 
-impl pallet_balances::Trait for RuntimeReferendum {
+impl balances::Trait for RuntimeReferendum {
     type Balance = u64;
     type Event = TestReferendumEvent;
     type DustRemoval = ();
@@ -456,7 +458,7 @@ where
 
     // topup currency to the account
     fn topup_account(account_id: u64, amount: BalanceReferendum<T>) {
-        let _ = pallet_balances::Module::<RuntimeReferendum>::deposit_creating(
+        let _ = balances::Module::<RuntimeReferendum>::deposit_creating(
             &account_id,
             amount.into(),
         );

+ 614 - 0
runtime-modules/council/src/staking_handler.rs

@@ -0,0 +1,614 @@
+// TODO: content of this file should be replaced by separate crate with StakingHandler
+
+use frame_support::dispatch::DispatchResult;
+use membership;
+use balances;
+
+/////////////////// Trait definition ///////////////////////////////////////////
+
+/// Member identifier in membership::member module.
+pub type MemberId<T> = <T as membership::Trait>::MemberId;
+
+/// Balance alias for GovernanceCurrency from `common` module. TODO: replace with BalanceOf
+pub type BalanceOf<T> = <T as balances::Trait>::Balance;
+
+// NOTE: this is copy of StakingHandler definition from `proposals_update` development branch
+/// 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.
+pub trait StakingHandler<T: system::Trait + membership::Trait + balances::Trait> {
+    /// Locks the specified balance on the account using specific lock identifier.
+    fn lock(account_id: &T::AccountId, amount: BalanceOf<T>);
+
+    /// Removes the specified lock on the account.
+    fn unlock(account_id: &T::AccountId);
+
+    /// Slash the specified balance on the account using specific lock identifier.
+    /// No limits, no actions on zero stake.
+    /// If slashing balance greater than the existing stake - stake is slashed to zero.
+    /// Returns actually slashed balance.
+    fn slash(account_id: &T::AccountId, amount: Option<BalanceOf<T>>) -> BalanceOf<T>;
+
+    /// 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;
+
+    /// Verifies that staking account balance is sufficient for staking.
+    /// During the balance check we should consider already locked stake. Effective balance to check
+    /// is 'already locked funds' + 'usable funds'.
+    fn is_enough_balance_for_stake(account_id: &T::AccountId, amount: BalanceOf<T>) -> bool;
+
+    /// Returns the current stake on the account.
+    fn current_stake(account_id: &T::AccountId) -> BalanceOf<T>;
+}
+
+// NOTE: this is
+// 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.
+pub trait StakingHandler2<AccountId, Balance, MemberId> {
+    /// Locks the specified balance on the account using specific lock identifier.
+    fn lock(account_id: &AccountId, amount: Balance);
+
+    /// Removes the specified lock on the account.
+    fn unlock(account_id: &AccountId);
+
+    /// Slash the specified balance on the account using specific lock identifier.
+    /// No limits, no actions on zero stake.
+    /// If slashing balance greater than the existing stake - stake is slashed to zero.
+    /// Returns actually slashed balance.
+    fn slash(account_id: &AccountId, amount: Option<Balance>) -> Balance;
+
+    /// Sets the new stake to a given amount.
+    //fn set_stake(account_id: &AccountId, new_stake: Balance) -> DispatchResult;
+    fn set_stake(account_id: &AccountId, new_stake: Balance);
+
+    /// Verifies that staking account bound to the member.
+    fn is_member_staking_account(member_id: &MemberId, account_id: &AccountId) -> bool;
+
+    /// Verifies that there no conflicting stakes on the staking account.
+    fn is_account_free_of_conflicting_stakes(account_id: &AccountId) -> bool;
+
+    /// Verifies that staking account balance is sufficient for staking.
+    /// During the balance check we should consider already locked stake. Effective balance to check
+    /// is 'already locked funds' + 'usable funds'.
+    fn is_enough_balance_for_stake(account_id: &AccountId, amount: Balance) -> bool;
+
+    /// Returns the current stake on the account.
+    fn current_stake(account_id: &AccountId) -> Balance;
+}
+
+/////////////////// Mock implementation ////////////////////////////////////////
+
+#[cfg(test)]
+pub mod mocks {
+    use common::currency::GovernanceCurrency;
+    use frame_support::traits::{WithdrawReasons, LockIdentifier, LockableCurrency, Currency};
+    use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+    use frame_support::dispatch::{DispatchResult};
+    use super::{StakingHandler, StakingHandler2, BalanceOf};
+    use sp_runtime::{
+        testing::Header,
+        traits::{BlakeTwo256, IdentityLookup},
+        Perbill,
+    };
+    use sp_core::H256;
+
+    pub type BalanceOfCurrency<T> =
+        <<T as common::currency::GovernanceCurrency>::Currency as Currency<
+            <T as system::Trait>::AccountId,
+        >>::Balance;
+
+
+    pub const STAKING_ACCOUNT_ID_FOR_FAILED_VALIDITY_CHECK: u64 = 111;
+    pub const STAKING_ACCOUNT_ID_FOR_FAILED_AMOUNT_CHECK: u64 = 222;
+    pub const STAKING_ACCOUNT_ID_FOR_CONFLICTING_STAKES: u64 = 333;
+    pub const STAKING_ACCOUNT_ID_FOR_ZERO_STAKE: u64 = 444;
+    pub const LOCK_ID_1: LockIdentifier = [1; 8];
+    pub const LOCK_ID_2: LockIdentifier = [1; 8];
+
+    // Workaround for https://github.com/rust-lang/rust/issues/26925 - remove when sorted.
+    //#[derive(Clone, PartialEq, Eq, Debug)]
+    //pub struct Lock1;
+
+    #[derive(Clone, PartialEq, Eq, Debug)]
+    pub struct Lock1;
+    #[derive(Clone, PartialEq, Eq, Debug)]
+    pub struct Lock2;
+
+    //pub type Membership = membership::Module<Lock1>;
+    pub type Balances = balances::Module<Lock1>;
+    pub type System = system::Module<Lock1>;
+
+
+    /////////////////// Lock1 //////////////////////////////////////////////////
+
+    impl StakingHandler<Lock1> for Lock1 {
+        fn lock(account_id: &<Lock1 as system::Trait>::AccountId, amount: BalanceOfCurrency<Lock1>) {
+            <Lock1 as GovernanceCurrency>::Currency::set_lock(
+                LOCK_ID_1,
+                &account_id,
+                amount,
+                WithdrawReasons::all(),
+            )
+        }
+
+        fn unlock(account_id: &<Lock1 as system::Trait>::AccountId) {
+            <Lock1 as GovernanceCurrency>::Currency::remove_lock(LOCK_ID_1, &account_id);
+        }
+
+        fn slash(
+            account_id: &<Lock1 as system::Trait>::AccountId,
+            amount: Option<BalanceOfCurrency<Lock1>>,
+        ) -> BalanceOfCurrency<Lock1> {
+            let locks = Balances::locks(&account_id);
+
+            let existing_lock = locks.iter().find(|lock| lock.id == LOCK_ID_1);
+
+            let mut actually_slashed_balance = Default::default();
+            if let Some(existing_lock) = existing_lock {
+                <Self as StakingHandler<Self>>::unlock(&account_id);
+
+                let mut slashable_amount = existing_lock.amount;
+                if let Some(amount) = amount {
+                    if existing_lock.amount > amount {
+                        let new_amount = existing_lock.amount - amount;
+                        <Self as StakingHandler<Self>>::lock(&account_id, new_amount);
+
+                        slashable_amount = amount;
+                    }
+                }
+
+                let _ = Balances::slash(&account_id, slashable_amount);
+
+                actually_slashed_balance = slashable_amount
+            }
+
+            actually_slashed_balance
+        }
+
+        fn set_stake(account_id: &<Lock2 as system::Trait>::AccountId, new_stake: BalanceOf<Lock2>) -> DispatchResult {
+            <Self as StakingHandler<Self>>::unlock(account_id);
+            <Self as StakingHandler<Self>>::lock(account_id, new_stake);
+
+            Ok(())
+        }
+
+        fn is_member_staking_account(_member_id: &u64, account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_VALIDITY_CHECK {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_account_free_of_conflicting_stakes(account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_CONFLICTING_STAKES {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_enough_balance_for_stake(account_id: &u64, amount: u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_AMOUNT_CHECK || amount > 1000 {
+                return false;
+            }
+
+            true
+        }
+
+        fn current_stake(account_id: &u64) -> u64 {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_ZERO_STAKE {
+                return 0;
+            }
+
+            100 // random non-zero value
+        }
+    }
+
+    /////////////////// Tmp ////////////////////////////////////////////////////
+
+    impl StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId> for Lock1 {
+        fn lock(account_id: &<Lock1 as system::Trait>::AccountId, amount: BalanceOfCurrency<Lock1>) {
+            <Lock1 as GovernanceCurrency>::Currency::set_lock(
+                LOCK_ID_1,
+                &account_id,
+                amount,
+                WithdrawReasons::all(),
+            )
+        }
+
+        fn unlock(account_id: &<Lock1 as system::Trait>::AccountId) {
+            <Lock1 as GovernanceCurrency>::Currency::remove_lock(LOCK_ID_1, &account_id);
+        }
+
+        fn slash(
+            account_id: &<Lock1 as system::Trait>::AccountId,
+            amount: Option<BalanceOfCurrency<Lock1>>,
+        ) -> BalanceOfCurrency<Lock1> {
+            let locks = Balances::locks(&account_id);
+
+            let existing_lock = locks.iter().find(|lock| lock.id == LOCK_ID_1);
+
+            let mut actually_slashed_balance = Default::default();
+            if let Some(existing_lock) = existing_lock {
+                <Self as StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId>>::unlock(&account_id);
+
+                let mut slashable_amount = existing_lock.amount;
+                if let Some(amount) = amount {
+                    if existing_lock.amount > amount {
+                        let new_amount = existing_lock.amount - amount;
+                        <Self as StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId>>::lock(&account_id, new_amount);
+
+                        slashable_amount = amount;
+                    }
+                }
+
+                let _ = Balances::slash(&account_id, slashable_amount);
+
+                actually_slashed_balance = slashable_amount
+            }
+
+            actually_slashed_balance
+        }
+
+        /*
+        fn set_stake(account_id: &<Lock2 as system::Trait>::AccountId, new_stake: BalanceOf<Lock2>) -> DispatchResult {
+            <Self as StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId>>::unlock(account_id);
+            <Self as StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId>>::lock(account_id, new_stake);
+
+            Ok(())
+        }
+        */
+        fn set_stake(account_id: &<Lock2 as system::Trait>::AccountId, new_stake: BalanceOf<Lock2>) {
+            <Self as StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId>>::unlock(account_id);
+            <Self as StakingHandler2<<Lock1 as system::Trait>::AccountId, BalanceOf<Lock1>, <Lock1 as membership::Trait>::MemberId>>::lock(account_id, new_stake);
+        }
+
+        fn is_member_staking_account(_member_id: &u64, account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_VALIDITY_CHECK {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_account_free_of_conflicting_stakes(account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_CONFLICTING_STAKES {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_enough_balance_for_stake(account_id: &u64, amount: u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_AMOUNT_CHECK || amount > 1000 {
+                return false;
+            }
+
+            true
+        }
+
+        fn current_stake(account_id: &u64) -> u64 {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_ZERO_STAKE {
+                return 0;
+            }
+
+            100 // random non-zero value
+        }
+    }
+
+    impl StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId> for Lock2 {
+        fn lock(account_id: &<Lock2 as system::Trait>::AccountId, amount: BalanceOfCurrency<Lock2>) {
+            <Lock2 as GovernanceCurrency>::Currency::set_lock(
+                LOCK_ID_2,
+                &account_id,
+                amount,
+                WithdrawReasons::all(),
+            )
+        }
+
+        fn unlock(account_id: &<Lock2 as system::Trait>::AccountId) {
+            <Lock2 as GovernanceCurrency>::Currency::remove_lock(LOCK_ID_2, &account_id);
+        }
+
+        fn slash(
+            account_id: &<Lock2 as system::Trait>::AccountId,
+            amount: Option<BalanceOfCurrency<Lock2>>,
+        ) -> BalanceOfCurrency<Lock2> {
+            let locks = Balances::locks(&account_id);
+
+            let existing_lock = locks.iter().find(|lock| lock.id == LOCK_ID_2);
+
+            let mut actually_slashed_balance = Default::default();
+            if let Some(existing_lock) = existing_lock {
+                <Self as StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId>>::unlock(&account_id);
+
+                let mut slashable_amount = existing_lock.amount;
+                if let Some(amount) = amount {
+                    if existing_lock.amount > amount {
+                        let new_amount = existing_lock.amount - amount;
+                        <Self as StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId>>::lock(&account_id, new_amount);
+
+                        slashable_amount = amount;
+                    }
+                }
+
+                let _ = Balances::slash(&account_id, slashable_amount);
+
+                actually_slashed_balance = slashable_amount
+            }
+
+            actually_slashed_balance
+        }
+
+        /*
+        fn set_stake(account_id: &<Lock2 as system::Trait>::AccountId, new_stake: BalanceOf<Lock2>) -> DispatchResult {
+            <Self as StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId>>::unlock(account_id);
+            <Self as StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId>>::lock(account_id, new_stake);
+
+            Ok(())
+        }
+        */
+        fn set_stake(account_id: &<Lock2 as system::Trait>::AccountId, new_stake: BalanceOf<Lock2>) {
+            <Self as StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId>>::unlock(account_id);
+            <Self as StakingHandler2<<Lock2 as system::Trait>::AccountId, BalanceOf<Lock2>, <Lock2 as membership::Trait>::MemberId>>::lock(account_id, new_stake);
+        }
+
+        fn is_member_staking_account(_member_id: &u64, account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_VALIDITY_CHECK {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_account_free_of_conflicting_stakes(account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_CONFLICTING_STAKES {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_enough_balance_for_stake(account_id: &u64, amount: u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_AMOUNT_CHECK || amount > 1000 {
+                return false;
+            }
+
+            true
+        }
+
+        fn current_stake(account_id: &u64) -> u64 {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_ZERO_STAKE {
+                return 0;
+            }
+
+            100 // random non-zero value
+        }
+    }
+
+    parameter_types! {
+        pub const BlockHashCount: u64 = 250;
+        pub const MaximumBlockWeight: u32 = 1024;
+        pub const MaximumBlockLength: u32 = 2 * 1024;
+        pub const AvailableBlockRatio: Perbill = Perbill::one();
+        pub const MinimumPeriod: u64 = 5;
+        pub const ExistentialDeposit: u32 = 0;
+    }
+
+    mod membership_mod {
+        pub use membership::Event;
+    }
+
+    impl_outer_event! {
+        pub enum TestEvent for Lock1 {
+            balances<T>,
+            membership_mod<T>,
+            system<T>,
+        }
+    }
+
+    impl membership::Trait for Lock1 {
+        type Event = TestEvent;
+        type MemberId = u64;
+        type PaidTermId = u64;
+        type SubscriptionId = u64;
+        type ActorId = u64;
+    }
+
+    impl timestamp::Trait for Lock1 {
+        type Moment = u64;
+        type OnTimestampSet = ();
+        type MinimumPeriod = MinimumPeriod;
+    }
+
+    impl common::currency::GovernanceCurrency for Lock1 {
+        type Currency = Balances;
+    }
+
+    impl balances::Trait for Lock1 {
+        type Balance = u64;
+        type DustRemoval = ();
+        type Event = TestEvent;
+        type ExistentialDeposit = ExistentialDeposit;
+        type AccountStore = System;
+    }
+
+    impl_outer_origin! {
+        pub enum Origin for Lock1 {}
+    }
+
+    impl system::Trait for Lock1 {
+        type BaseCallFilter = ();
+        type Origin = Origin;
+        type Index = u64;
+        type BlockNumber = u64;
+        type Call = ();
+        type Hash = H256;
+        type Hashing = BlakeTwo256;
+        type AccountId = u64;
+        type Lookup = IdentityLookup<Self::AccountId>;
+        type Header = Header;
+        type Event = TestEvent;
+        type BlockHashCount = BlockHashCount;
+        type MaximumBlockWeight = MaximumBlockWeight;
+        type DbWeight = ();
+        type BlockExecutionWeight = ();
+        type ExtrinsicBaseWeight = ();
+        type MaximumExtrinsicWeight = ();
+        type MaximumBlockLength = MaximumBlockLength;
+        type AvailableBlockRatio = AvailableBlockRatio;
+        type Version = ();
+        type ModuleToIndex = ();
+        type AccountData = balances::AccountData<u64>;
+        type OnNewAccount = ();
+        type OnKilledAccount = ();
+    }
+
+    /////////////////// Lock2 //////////////////////////////////////////////////
+
+    impl StakingHandler<Lock2> for Lock2 {
+        fn lock(account_id: &<Lock2 as system::Trait>::AccountId, amount: BalanceOfCurrency<Lock2>) {
+            <Lock2 as GovernanceCurrency>::Currency::set_lock(
+                LOCK_ID_1,
+                &account_id,
+                amount,
+                WithdrawReasons::all(),
+            )
+        }
+
+        fn unlock(account_id: &<Lock2 as system::Trait>::AccountId) {
+            <Lock2 as GovernanceCurrency>::Currency::remove_lock(LOCK_ID_1, &account_id);
+        }
+
+        fn slash(
+            account_id: &<Lock2 as system::Trait>::AccountId,
+            amount: Option<BalanceOfCurrency<Lock2>>,
+        ) -> BalanceOfCurrency<Lock2> {
+            let locks = Balances::locks(&account_id);
+
+            let existing_lock = locks.iter().find(|lock| lock.id == LOCK_ID_1);
+
+            let mut actually_slashed_balance = Default::default();
+            if let Some(existing_lock) = existing_lock {
+                <Self as StakingHandler<Self>>::unlock(&account_id);
+
+                let mut slashable_amount = existing_lock.amount;
+                if let Some(amount) = amount {
+                    if existing_lock.amount > amount {
+                        let new_amount = existing_lock.amount - amount;
+                        <Self as StakingHandler<Self>>::lock(&account_id, new_amount);
+
+                        slashable_amount = amount;
+                    }
+                }
+
+                let _ = Balances::slash(&account_id, slashable_amount);
+
+                actually_slashed_balance = slashable_amount
+            }
+
+            actually_slashed_balance
+        }
+
+        fn set_stake(account_id: &<Lock2 as system::Trait>::AccountId, new_stake: BalanceOf<Lock2>) -> DispatchResult {
+            <Self as StakingHandler<Self>>::unlock(account_id);
+            <Self as StakingHandler<Self>>::lock(account_id, new_stake);
+
+            Ok(())
+        }
+
+        fn is_member_staking_account(_member_id: &u64, account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_VALIDITY_CHECK {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_account_free_of_conflicting_stakes(account_id: &u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_CONFLICTING_STAKES {
+                return false;
+            }
+
+            true
+        }
+
+        fn is_enough_balance_for_stake(account_id: &u64, amount: u64) -> bool {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_FAILED_AMOUNT_CHECK || amount > 1000 {
+                return false;
+            }
+
+            true
+        }
+
+        fn current_stake(account_id: &u64) -> u64 {
+            if *account_id == STAKING_ACCOUNT_ID_FOR_ZERO_STAKE {
+                return 0;
+            }
+
+            100 // random non-zero value
+        }
+    }
+
+    impl membership::Trait for Lock2 {
+        type Event = TestEvent;
+        type MemberId = u64;
+        type PaidTermId = u64;
+        type SubscriptionId = u64;
+        type ActorId = u64;
+    }
+
+    impl timestamp::Trait for Lock2 {
+        type Moment = u64;
+        type OnTimestampSet = ();
+        type MinimumPeriod = MinimumPeriod;
+    }
+
+    impl common::currency::GovernanceCurrency for Lock2 {
+        type Currency = Balances;
+    }
+
+    impl balances::Trait for Lock2 {
+        type Balance = u64;
+        type DustRemoval = ();
+        type Event = TestEvent;
+        type ExistentialDeposit = ExistentialDeposit;
+        type AccountStore = System;
+    }
+
+    impl system::Trait for Lock2 {
+        type BaseCallFilter = ();
+        type Origin = Origin;
+        type Index = u64;
+        type BlockNumber = u64;
+        type Call = ();
+        type Hash = H256;
+        type Hashing = BlakeTwo256;
+        type AccountId = u64;
+        type Lookup = IdentityLookup<Self::AccountId>;
+        type Header = Header;
+        type Event = TestEvent;
+        type BlockHashCount = BlockHashCount;
+        type MaximumBlockWeight = MaximumBlockWeight;
+        type DbWeight = ();
+        type BlockExecutionWeight = ();
+        type ExtrinsicBaseWeight = ();
+        type MaximumExtrinsicWeight = ();
+        type MaximumBlockLength = MaximumBlockLength;
+        type AvailableBlockRatio = AvailableBlockRatio;
+        type Version = ();
+        type ModuleToIndex = ();
+        type AccountData = balances::AccountData<u64>;
+        type OnNewAccount = ();
+        type OnKilledAccount = ();
+    }
+
+}

+ 2 - 2
runtime-modules/referendum/src/lib.rs

@@ -174,9 +174,9 @@ pub trait Trait<I: Instance>: system::Trait {
         + MaybeSerialize
         + PartialEq;
 
-    /// Duration of voting stage (in blocks)
+    /// Duration of voting stage (number of blocks)
     type VoteStageDuration: Get<Self::BlockNumber>;
-    /// Duration of revealing stage (in blocks)
+    /// Duration of revealing stage (number of blocks)
     type RevealStageDuration: Get<Self::BlockNumber>;
 
     /// Minimum stake needed for voting