Browse Source

Merge pull request #1815 from ondratra/council_winner_vote_stakes

council - winner vote stakes longer
Bedeho Mender 4 years ago
parent
commit
1fe64dba4e

+ 46 - 22
runtime-modules/council/src/lib.rs

@@ -36,7 +36,7 @@
 //! ## Important functions
 //! These functions have to be called by the runtime for the council to work properly.
 //! - [recieve_referendum_results](./trait.ReferendumConnection.html#method.recieve_referendum_results)
-//! - [can_release_vote_stake](./trait.ReferendumConnection.html#method.can_release_vote_stake)
+//! - [can_unlock_vote_stake](./trait.ReferendumConnection.html#method.can_unlock_vote_stake)
 //!
 //! ## Dependencies:
 //! - [referendum](../referendum/index.html)
@@ -61,7 +61,7 @@ use sp_runtime::traits::{Hash, MaybeSerialize, Member, SaturatedConversion, Satu
 use std::marker::PhantomData;
 use system::{ensure_root, ensure_signed};
 
-use referendum::{OptionResult, ReferendumManager};
+use referendum::{CastVote, OptionResult, ReferendumManager};
 
 // declared modules
 mod mock;
@@ -174,6 +174,7 @@ pub type VotePowerOf<T> = <<T as Trait>::Referendum as ReferendumManager<
     <T as system::Trait>::AccountId,
     <T as system::Trait>::Hash,
 >>::VotePower;
+pub type CastVoteOf<T> = CastVote<<T as system::Trait>::Hash, Balance<T>>;
 
 pub type CouncilMemberOf<T> = CouncilMember<
     <T as system::Trait>::AccountId,
@@ -201,7 +202,8 @@ pub trait Trait: system::Trait {
         + Copy
         + MaybeSerialize
         + PartialEq
-        + From<u64>;
+        + From<u64>
+        + Into<u64>;
 
     /// Referendum used for council elections.
     type Referendum: ReferendumManager<Self::Origin, Self::AccountId, Self::Hash>;
@@ -247,7 +249,7 @@ pub trait ReferendumConnection<T: Trait> {
     fn recieve_referendum_results(winners: &[OptionResult<VotePowerOf<T>>]);
 
     /// Process referendum results. This function MUST be called in runtime's implementation of referendum's `can_release_voting_stake()`.
-    fn can_release_vote_stake() -> Result<(), Error<T>>;
+    fn can_unlock_vote_stake(vote: &CastVoteOf<T>) -> Result<(), Error<T>>;
 
     /// Checks that user is indeed candidating. This function MUST be called in runtime's implementation of referendum's `is_valid_option_id()`.
     fn is_valid_candidate_id(membership_id: &T::MembershipId) -> bool;
@@ -677,18 +679,10 @@ impl<T: Trait> Module<T> {
                 let unpaid_reward =
                     Calculations::<T>::get_current_reward(&council_member, reward_per_block, now);
 
-                if unpaid_reward == 0.into() {
-                    Self::deposit_event(RawEvent::RewardPayment(
-                        council_member.membership_id,
-                        council_member.reward_account_id.clone(),
-                        0.into(),
-                        0.into(),
-                    ));
-                    return balance;
-                }
+                // depleted budget or no accumulated reward to be paid?
+                if balance == 0.into() || unpaid_reward == 0.into() {
+                    // no need to update council member record here; their unpaid reward will be recalculated next time rewards are paid
 
-                // stop iterating if budget is completely depleted
-                if balance == 0.into() {
                     // emit event
                     Self::deposit_event(RawEvent::RewardPayment(
                         council_member.membership_id,
@@ -759,12 +753,42 @@ impl<T: Trait> ReferendumConnection<T> for Module<T> {
     }
 
     /// Check that it is a proper time to release stake.
-    fn can_release_vote_stake() -> Result<(), Error<T>> {
-        // ensure it's proper time to release stake
-        match Stage::<T>::get().stage {
-            CouncilStage::Idle => (),
-            _ => return Err(Error::CantReleaseStakeNow),
-        };
+    fn can_unlock_vote_stake(vote: &CastVoteOf<T>) -> Result<(), Error<T>> {
+        let current_voting_cycle_id = AnnouncementPeriodNr::get();
+
+        // allow release for very old votes
+        if current_voting_cycle_id > vote.cycle_id + 1 {
+            return Ok(());
+        }
+
+        // allow release for current cycle only in idle stage
+        if current_voting_cycle_id == vote.cycle_id
+            && !matches!(Stage::<T>::get().stage, CouncilStage::Idle)
+        {
+            return Err(Error::CantReleaseStakeNow);
+        }
+
+        let voting_for_winner = CouncilMembers::<T>::get()
+            .iter()
+            .map(|council_member| council_member.membership_id)
+            .any(|membership_id| vote.vote_for == Some(membership_id.into()));
+
+        // allow release for vote from previous elections only when not voted for winner
+        if current_voting_cycle_id == vote.cycle_id + 1 {
+            // ensure vote was not cast for the one of winning candidates / council members
+            if voting_for_winner {
+                return Err(Error::CantReleaseStakeNow);
+            }
+
+            return Ok(());
+        }
+
+        // at this point vote.cycle_id == current_voting_cycle_id
+
+        // ensure election has ended and voter haven't voted for winner
+        if voting_for_winner || !matches!(Stage::<T>::get().stage, CouncilStage::Idle) {
+            return Err(Error::CantReleaseStakeNow);
+        }
 
         Ok(())
     }
@@ -853,7 +877,7 @@ impl<T: Trait> Mutations<T> {
         let extra_winning_target_count = T::CouncilSize::get() - 1;
 
         // start referendum
-        T::Referendum::force_start(extra_winning_target_count);
+        T::Referendum::force_start(extra_winning_target_count, AnnouncementPeriodNr::get());
 
         let block_number = <system::Module<T>>::block_number();
 

+ 30 - 8
runtime-modules/council/src/mock.rs

@@ -15,8 +15,8 @@ use frame_support::{
 };
 use rand::Rng;
 use referendum::{
-    Balance as BalanceReferendum, CastVote, CurrentCycleId, OptionResult, ReferendumManager,
-    ReferendumStage, ReferendumStageRevealing,
+    Balance as BalanceReferendum, CastVote, OptionResult, ReferendumManager, ReferendumStage,
+    ReferendumStageRevealing,
 };
 use sp_core::H256;
 use sp_io;
@@ -213,15 +213,15 @@ impl referendum::Trait<ReferendumInstance> for RuntimeReferendum {
         stake
     }
 
-    fn can_release_voting_stake(
-        _vote: &CastVote<Self::Hash, BalanceReferendum<Self, ReferendumInstance>>,
+    fn can_unlock_vote_stake(
+        vote: &CastVote<Self::Hash, BalanceReferendum<Self, ReferendumInstance>>,
     ) -> bool {
         // trigger fail when requested to do so
         if !IS_UNSTAKE_ENABLED.with(|value| value.borrow().0) {
             return false;
         }
 
-        <Module<Runtime> as ReferendumConnection<Runtime>>::can_release_vote_stake().is_ok()
+        <Module<Runtime> as ReferendumConnection<Runtime>>::can_unlock_vote_stake(vote).is_ok()
     }
 
     fn process_results(winners: &[OptionResult<Self::VotePower>]) {
@@ -519,10 +519,16 @@ where
         }
     }
 
-    pub fn generate_voter(index: u64, stake: Balance<T>, vote_for_index: u64) -> VoterInfo<T> {
+    pub fn generate_voter(
+        index: u64,
+        stake: Balance<T>,
+        vote_for_index: u64,
+        cycle_id: u64,
+    ) -> VoterInfo<T> {
         let account_id = VOTER_BASE_ID + index;
         let origin = OriginType::Signed(account_id.into());
-        let (commitment, salt) = Self::vote_commitment(&account_id.into(), &vote_for_index.into());
+        let (commitment, salt) =
+            Self::vote_commitment(&account_id.into(), &vote_for_index.into(), &cycle_id);
 
         Self::topup_account(account_id.into(), stake);
 
@@ -545,8 +551,8 @@ where
     pub fn vote_commitment(
         account_id: &<T as system::Trait>::AccountId,
         vote_option_index: &u64,
+        cycle_id: &u64,
     ) -> (T::Hash, Vec<u8>) {
-        let cycle_id = CurrentCycleId::<ReferendumInstance>::get();
         let salt = Self::generate_salt();
 
         (
@@ -648,6 +654,7 @@ where
                         vote_power: item.vote_power.into(),
                     })
                     .collect(),
+                current_cycle_id: AnnouncementPeriodNr::get(),
             }),
         );
 
@@ -763,6 +770,20 @@ where
         );
     }
 
+    pub fn release_vote_stake(
+        origin: OriginType<<Runtime as system::Trait>::AccountId>,
+        expected_result: Result<(), ()>,
+    ) -> () {
+        // check method returns expected result
+        assert_eq!(
+            referendum::Module::<RuntimeReferendum, ReferendumInstance>::release_vote_stake(
+                InstanceMockUtils::<Runtime>::mock_origin(origin),
+            )
+            .is_ok(),
+            expected_result.is_ok(),
+        );
+    }
+
     pub fn set_budget(
         origin: OriginType<T::AccountId>,
         amount: Balance<T>,
@@ -977,6 +998,7 @@ where
                     index as u64 + users_offset,
                     vote_stake.into(),
                     CANDIDATE_BASE_ID + votes_map[index] + users_offset,
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();

+ 45 - 1
runtime-modules/council/src/tests.rs

@@ -1,7 +1,8 @@
 #![cfg(test)]
 
 use super::{
-    Budget, CouncilMemberOf, CouncilMembers, CouncilStageAnnouncing, Error, Module, Trait,
+    AnnouncementPeriodNr, Budget, CouncilMemberOf, CouncilMembers, CouncilStageAnnouncing, Error,
+    Module, Trait,
 };
 use crate::mock::*;
 use crate::staking_handler::mocks::{CANDIDATE_BASE_ID, VOTER_CANDIDATE_OFFSET};
@@ -133,6 +134,7 @@ fn council_can_vote_for_yourself() {
             VOTER_CANDIDATE_OFFSET,
             vote_stake,
             self_voting_candidate_index,
+            AnnouncementPeriodNr::get(),
         );
         Mocks::vote_for_candidate(
             voter.origin.clone(),
@@ -153,6 +155,40 @@ fn council_can_vote_for_yourself() {
     });
 }
 
+/// Test that vote for a succesfull candidate has it's stake locked until the one referendum cycle with succesfull council election
+#[test]
+fn council_vote_for_winner_stakes_longer() {
+    let config = default_genesis_config();
+
+    build_test_externalities(config).execute_with(|| {
+        let council_settings = CouncilSettings::<Runtime>::extract_settings();
+
+        // run first election round
+        let params = Mocks::run_full_council_cycle(0, &[], 0);
+        let second_round_user_offset = 100; // some number higher than the number of voters
+
+        let voter_for_winner = params.voters[0].clone();
+        let voter_for_looser = params.voters[params.voters.len() - 1].clone();
+
+        // try to release vote stake
+        Mocks::release_vote_stake(voter_for_winner.origin.clone(), Err(()));
+        Mocks::release_vote_stake(voter_for_looser.origin.clone(), Ok(()));
+
+        // try to release vote stake
+        Mocks::release_vote_stake(voter_for_winner.origin.clone(), Err(()));
+
+        // run second election round
+        Mocks::run_full_council_cycle(
+            council_settings.cycle_duration,
+            &params.expected_final_council_members,
+            second_round_user_offset,
+        );
+
+        // try to release vote stake
+        Mocks::release_vote_stake(voter_for_winner.origin.clone(), Ok(()));
+    });
+}
+
 // Test that only valid members can candidate.
 #[test]
 fn council_candidacy_invalid_member() {
@@ -249,6 +285,7 @@ fn council_announcement_reset_on_not_enough_winners() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -340,6 +377,7 @@ fn council_two_consecutive_rounds() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -365,6 +403,7 @@ fn council_two_consecutive_rounds() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map2[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -529,6 +568,7 @@ fn council_candidate_stake_can_be_unlocked() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -635,6 +675,7 @@ fn council_candidate_stake_automaticly_converted() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -725,6 +766,7 @@ fn council_member_stake_is_locked() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -782,6 +824,7 @@ fn council_member_stake_automaticly_unlocked() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map2[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();
@@ -863,6 +906,7 @@ fn council_candidacy_set_note() {
                     index as u64,
                     vote_stake,
                     CANDIDATE_BASE_ID + votes_map[index],
+                    AnnouncementPeriodNr::get(),
                 )
             })
             .collect();

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

@@ -23,7 +23,7 @@
 //!
 //! - [vote](./struct.Module.html#method.vote)
 //! - [reveal_vote](./struct.Module.html#method.reveal_vote)
-//! - [release_voting_stake](./struct.Module.html#method.release_voting_stake)
+//! - [release_vote_stake](./struct.Module.html#method.release_vote_stake)
 //!
 //! ## Notes
 //! This module is instantiable pallet as described here https://substrate.dev/recipes/3-entrees/instantiable.html
@@ -71,8 +71,9 @@ impl<BlockNumber, VotePower: Encode + Decode> Default for ReferendumStage<BlockN
 /// Representation for voting stage state.
 #[derive(Encode, Decode, PartialEq, Eq, Debug, Default)]
 pub struct ReferendumStageVoting<BlockNumber> {
-    started: BlockNumber,      // block in which referendum started
-    winning_target_count: u64, // target number of winners
+    pub started: BlockNumber,      // block in which referendum started
+    pub winning_target_count: u64, // target number of winners
+    pub current_cycle_id: u64,     // index of current election
 }
 
 /// Representation for revealing stage state.
@@ -81,6 +82,7 @@ pub struct ReferendumStageRevealing<BlockNumber, VotePower> {
     pub started: BlockNumber,      // block in which referendum started
     pub winning_target_count: u64, // target number of winners
     pub intermediate_winners: Vec<OptionResult<VotePower>>, // intermediate winning options
+    pub current_cycle_id: u64,     // index of current election
 }
 
 #[derive(Encode, Decode, PartialEq, Eq, Debug, Default, Clone)]
@@ -92,10 +94,10 @@ pub struct OptionResult<VotePower> {
 /// Vote cast in referendum. Vote target is concealed until user reveals commitment's proof.
 #[derive(Encode, Decode, PartialEq, Eq, Debug, Default)]
 pub struct CastVote<Hash, Currency> {
-    commitment: Hash, // a commitment that a user submits in the voting stage before revealing what this vote is actually for
-    cycle_id: u64,    // current referendum cycle number
-    stake: Currency,  // stake locked for vote
-    vote_for: Option<u64>, // target option this vote favors; is `None` before the vote is revealed
+    pub commitment: Hash, // a commitment that a user submits in the voting stage before revealing what this vote is actually for
+    pub cycle_id: u64,    // current referendum cycle number
+    pub stake: Currency,  // stake locked for vote
+    pub vote_for: Option<u64>, // target option this vote favors; is `None` before the vote is revealed
 }
 
 /////////////////// Type aliases ///////////////////////////////////////////////
@@ -136,11 +138,15 @@ pub trait ReferendumManager<Origin, AccountId, Hash> {
     type Currency: LockableCurrency<AccountId>;
 
     /// Start a new referendum.
-    fn start_referendum(origin: Origin, extra_winning_target_count: u64) -> Result<(), ()>;
+    fn start_referendum(
+        origin: Origin,
+        extra_winning_target_count: u64,
+        cycle_id: u64,
+    ) -> Result<(), ()>;
 
     /// Start referendum independent of the current state.
     /// If an election is running before calling this function, it will be discontinued without any winners selected.
-    fn force_start(extra_winning_target_count: u64);
+    fn force_start(extra_winning_target_count: u64, cycle_id: u64);
 
     /// Calculate commitment for a vote.
     fn calculate_commitment(
@@ -194,7 +200,7 @@ pub trait Trait<I: Instance>: system::Trait {
 
     /// Checks if user can unlock his stake from the given vote.
     /// Gives runtime an ability to penalize user for not revealing stake, etc.
-    fn can_release_voting_stake(vote: &CastVote<Self::Hash, Balance<Self, I>>) -> bool;
+    fn can_unlock_vote_stake(vote: &CastVote<Self::Hash, Balance<Self, I>>) -> bool;
 
     /// Gives runtime an ability to react on referendum result.
     fn process_results(winners: &[OptionResult<Self::VotePower>]);
@@ -219,9 +225,6 @@ decl_storage! {
         /// A record is finally removed when the user unstakes, which can happen during a voting stage or after the current cycle ends.
         /// A stake for a vote can be reused in future referendum cycles.
         pub Votes get(fn votes) config(): map hasher(blake2_128_concat) T::AccountId => CastVoteOf<T, I>;
-
-        /// Index of the current referendum cycle. It is incremented everytime referendum ends.
-        pub CurrentCycleId get(fn current_cycle_id) config(): u64;
     }
 }
 
@@ -332,14 +335,14 @@ decl_module! {
         #[weight = 10_000_000]
         pub fn vote(origin, commitment: T::Hash, stake: Balance<T, I>) -> Result<(), Error<T, I>> {
             // ensure action can be started
-            let account_id = EnsureChecks::<T, I>::can_vote(origin, &stake)?;
+            let (current_cycle_id, account_id) = EnsureChecks::<T, I>::can_vote(origin, &stake)?;
 
             //
             // == MUTATION SAFE ==
             //
 
             // start revealing phase - it can return error when stake fails to lock
-            Mutations::<T, I>::vote(&account_id, &commitment, &stake)?;
+            Mutations::<T, I>::vote(&account_id, &commitment, &stake, &current_cycle_id)?;
 
             // emit event
             Self::deposit_event(RawEvent::VoteCast(account_id, commitment, stake));
@@ -367,15 +370,15 @@ decl_module! {
 
         /// Release a locked stake.
         #[weight = 10_000_000]
-        pub fn release_voting_stake(origin) -> Result<(), Error<T, I>> {
-            let account_id = EnsureChecks::<T, I>::can_release_voting_stake(origin)?;
+        pub fn release_vote_stake(origin) -> Result<(), Error<T, I>> {
+            let account_id = EnsureChecks::<T, I>::can_release_vote_stake(origin)?;
 
             //
             // == MUTATION SAFE ==
             //
 
             // reveal the vote - it can return error when stake fails to unlock
-            Mutations::<T, I>::release_voting_stake(&account_id);
+            Mutations::<T, I>::release_vote_stake(&account_id);
 
             // emit event
             Self::deposit_event(RawEvent::StakeReleased(account_id));
@@ -436,7 +439,11 @@ impl<T: Trait<I>, I: Instance> ReferendumManager<T::Origin, T::AccountId, T::Has
     type Currency = T::Currency;
 
     /// Start new referendum run.
-    fn start_referendum(origin: T::Origin, extra_winning_target_count: u64) -> Result<(), ()> {
+    fn start_referendum(
+        origin: T::Origin,
+        extra_winning_target_count: u64,
+        cycle_id: u64,
+    ) -> Result<(), ()> {
         let winning_target_count = extra_winning_target_count + 1;
 
         // ensure action can be started
@@ -447,7 +454,7 @@ impl<T: Trait<I>, I: Instance> ReferendumManager<T::Origin, T::AccountId, T::Has
         //
 
         // update state
-        Mutations::<T, I>::start_voting_period(&winning_target_count);
+        Mutations::<T, I>::start_voting_period(&winning_target_count, &cycle_id);
 
         // emit event
         Self::deposit_event(RawEvent::ReferendumStarted(winning_target_count));
@@ -457,14 +464,14 @@ impl<T: Trait<I>, I: Instance> ReferendumManager<T::Origin, T::AccountId, T::Has
 
     /// Start referendum independent of the current state.
     /// If an election is running before calling this function, it will be discontinued without any winners selected.
-    fn force_start(extra_winning_target_count: u64) {
+    fn force_start(extra_winning_target_count: u64, cycle_id: u64) {
         let winning_target_count = extra_winning_target_count + 1;
 
         // remember if referendum is running
         let referendum_running = !matches!(Stage::<T, I>::get(), ReferendumStage::Inactive);
 
         // update state
-        Mutations::<T, I>::start_voting_period(&winning_target_count);
+        Mutations::<T, I>::start_voting_period(&winning_target_count, &cycle_id);
 
         // emit event
         if referendum_running {
@@ -502,13 +509,14 @@ struct Mutations<T: Trait<I>, I: Instance> {
 
 impl<T: Trait<I>, I: Instance> Mutations<T, I> {
     /// Change the referendum stage from inactive to voting stage.
-    fn start_voting_period(winning_target_count: &u64) {
+    fn start_voting_period(winning_target_count: &u64, cycle_id: &u64) {
         // change referendum state
         Stage::<T, I>::put(ReferendumStage::Voting(ReferendumStageVoting::<
             T::BlockNumber,
         > {
             started: <system::Module<T>>::block_number() + 1.into(),
             winning_target_count: *winning_target_count,
+            current_cycle_id: *cycle_id,
         }));
     }
 
@@ -522,6 +530,7 @@ impl<T: Trait<I>, I: Instance> Mutations<T, I> {
             started: <system::Module<T>>::block_number() + 1.into(),
             winning_target_count: old_stage.winning_target_count,
             intermediate_winners: vec![],
+            current_cycle_id: old_stage.current_cycle_id,
         }));
     }
 
@@ -530,23 +539,18 @@ impl<T: Trait<I>, I: Instance> Mutations<T, I> {
         revealing_stage: ReferendumStageRevealingOf<T, I>,
     ) -> Vec<OptionResult<<T as Trait<I>>::VotePower>> {
         // reset referendum state
-        Self::reset_referendum();
+        Stage::<T, I>::put(ReferendumStage::Inactive);
 
         // return winning option
         revealing_stage.intermediate_winners
     }
 
-    /// Change the referendum stage from revealing to the inactive stage.
-    fn reset_referendum() {
-        Stage::<T, I>::put(ReferendumStage::Inactive);
-        CurrentCycleId::<I>::put(CurrentCycleId::<I>::get() + 1);
-    }
-
     /// Cast a user's sealed vote for the current referendum cycle.
     fn vote(
         account_id: &<T as system::Trait>::AccountId,
         commitment: &T::Hash,
         stake: &Balance<T, I>,
+        current_cycle_id: &u64,
     ) -> Result<(), Error<T, I>> {
         // lock stake amount
         T::Currency::set_lock(
@@ -562,7 +566,7 @@ impl<T: Trait<I>, I: Instance> Mutations<T, I> {
             CastVote {
                 commitment: *commitment,
                 stake: *stake,
-                cycle_id: CurrentCycleId::<I>::get(),
+                cycle_id: *current_cycle_id,
                 vote_for: None,
             },
         );
@@ -607,7 +611,7 @@ impl<T: Trait<I>, I: Instance> Mutations<T, I> {
     }
 
     /// Release stake associated to the user's last vote.
-    fn release_voting_stake(account_id: &<T as system::Trait>::AccountId) {
+    fn release_vote_stake(account_id: &<T as system::Trait>::AccountId) {
         // lock stake amount
         T::Currency::remove_lock(T::LockId::get(), account_id);
 
@@ -731,8 +735,12 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
         Ok(())
     }
 
-    fn can_vote(origin: T::Origin, stake: &Balance<T, I>) -> Result<T::AccountId, Error<T, I>> {
+    fn can_vote(
+        origin: T::Origin,
+        stake: &Balance<T, I>,
+    ) -> Result<(u64, T::AccountId), Error<T, I>> {
         fn prevent_repeated_vote<T: Trait<I>, I: Instance>(
+            cycle_id: &u64,
             account_id: &T::AccountId,
         ) -> Result<(), Error<T, I>> {
             if !Votes::<T, I>::contains_key(&account_id) {
@@ -742,7 +750,7 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
             let existing_vote = Votes::<T, I>::get(&account_id);
 
             // don't allow repeated vote
-            if existing_vote.cycle_id == CurrentCycleId::<I>::get() {
+            if existing_vote.cycle_id == *cycle_id {
                 return Err(Error::<T, I>::AlreadyVotedThisCycle);
             }
 
@@ -752,16 +760,14 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
         // ensure superuser requested action
         let account_id = Self::ensure_regular_user(origin)?;
 
-        let stage = Stage::<T, I>::get();
-
         // ensure referendum is running
-        match stage {
-            ReferendumStage::Voting(_) => (),
+        let current_cycle_id = match Stage::<T, I>::get() {
+            ReferendumStage::Voting(tmp_stage_data) => tmp_stage_data.current_cycle_id,
             _ => return Err(Error::ReferendumNotRunning),
         };
 
         // prevent repeated vote
-        prevent_repeated_vote::<T, I>(&account_id)?;
+        prevent_repeated_vote::<T, I>(&current_cycle_id, &account_id)?;
 
         // ensure stake is enough for voting
         if stake < &T::MinimumStake::get() {
@@ -773,7 +779,7 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
             return Err(Error::InsufficientBalanceToStakeCurrency);
         }
 
-        Ok(account_id)
+        Ok((current_cycle_id, account_id))
     }
 
     fn can_reveal_vote<R: ReferendumManager<T::Origin, T::AccountId, T::Hash>>(
@@ -781,15 +787,11 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
         salt: &[u8],
         vote_option_id: &u64,
     ) -> Result<CanRevealResult<T, I>, Error<T, I>> {
-        let cycle_id = CurrentCycleId::<I>::get();
-
         // ensure superuser requested action
         let account_id = Self::ensure_regular_user(origin)?;
 
-        let stage = Stage::<T, I>::get();
-
         // ensure referendum is running
-        let stage_data = match stage {
+        let stage_data = match Stage::<T, I>::get() {
             ReferendumStage::Revealing(tmp_stage_data) => tmp_stage_data,
             _ => return Err(Error::RevealingNotInProgress),
         };
@@ -802,7 +804,7 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
         }
 
         // ensure vote was cast for the running referendum
-        if cycle_id != cast_vote.cycle_id {
+        if stage_data.current_cycle_id != cast_vote.cycle_id {
             return Err(Error::InvalidVote);
         }
 
@@ -812,7 +814,12 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
         }
 
         // ensure commitment corresponds to salt and vote option
-        let commitment = R::calculate_commitment(&account_id, salt, &cycle_id, vote_option_id);
+        let commitment = R::calculate_commitment(
+            &account_id,
+            salt,
+            &stage_data.current_cycle_id,
+            vote_option_id,
+        );
         if commitment != cast_vote.commitment {
             return Err(Error::InvalidReveal);
         }
@@ -820,21 +827,14 @@ impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
         Ok((stage_data, account_id, cast_vote))
     }
 
-    fn can_release_voting_stake(origin: T::Origin) -> Result<T::AccountId, Error<T, I>> {
-        let cycle_id = CurrentCycleId::<I>::get();
-
+    fn can_release_vote_stake(origin: T::Origin) -> Result<T::AccountId, Error<T, I>> {
         // ensure superuser requested action
         let account_id = Self::ensure_regular_user(origin)?;
 
         let cast_vote = Self::ensure_vote_exists(&account_id)?;
 
-        // allow release only for past cycles
-        if cycle_id == cast_vote.cycle_id {
-            return Err(Error::UnstakingVoteInSameCycle);
-        }
-
         // ask runtime if stake can be released
-        if !T::can_release_voting_stake(&cast_vote) {
+        if !T::can_unlock_vote_stake(&cast_vote) {
             return Err(Error::UnstakingForbidden);
         }
 

+ 24 - 16
runtime-modules/referendum/src/mock.rs

@@ -2,9 +2,8 @@
 
 /////////////////// Configuration //////////////////////////////////////////////
 use crate::{
-    Balance, CastVote, CurrentCycleId, Error, Instance, Module, OptionResult, RawEvent,
-    ReferendumManager, ReferendumStage, ReferendumStageRevealing, ReferendumStageVoting, Stage,
-    Trait, Votes,
+    Balance, CastVote, Error, Instance, Module, OptionResult, RawEvent, ReferendumManager,
+    ReferendumStage, ReferendumStageRevealing, ReferendumStageVoting, Stage, Trait, Votes,
 };
 
 use frame_support::traits::{Currency, LockIdentifier, OnFinalize};
@@ -91,7 +90,7 @@ impl Trait<Instance0> for Runtime {
         stake
     }
 
-    fn can_release_voting_stake(_vote: &CastVote<Self::Hash, Balance<Self, Instance0>>) -> bool {
+    fn can_unlock_vote_stake(_vote: &CastVote<Self::Hash, Balance<Self, Instance0>>) -> bool {
         // trigger fail when requested to do so
         if !IS_UNSTAKE_ENABLED.with(|value| value.borrow().0) {
             return false;
@@ -234,7 +233,6 @@ pub fn default_genesis_config() -> GenesisConfig<Runtime, Instance0> {
     GenesisConfig::<Runtime, Instance0> {
         stage: ReferendumStage::default(),
         votes: vec![],
-        current_cycle_id: 0,
     }
 }
 
@@ -315,8 +313,8 @@ where
     pub fn calculate_commitment(
         account_id: &<T as system::Trait>::AccountId,
         vote_option_index: &u64,
+        cycle_id: &u64,
     ) -> (T::Hash, Vec<u8>) {
-        let cycle_id = CurrentCycleId::<I>::get();
         Self::calculate_commitment_for_cycle(account_id, &cycle_id, vote_option_index, None)
     }
 
@@ -324,8 +322,8 @@ where
         account_id: &<T as system::Trait>::AccountId,
         vote_option_index: &u64,
         custom_salt: &[u8],
+        cycle_id: &u64,
     ) -> (T::Hash, Vec<u8>) {
-        let cycle_id = CurrentCycleId::<I>::get();
         Self::calculate_commitment_for_cycle(
             account_id,
             &cycle_id,
@@ -383,6 +381,7 @@ impl InstanceMocks<Runtime, Instance0> {
     pub fn start_referendum_extrinsic(
         origin: OriginType<<Runtime as system::Trait>::AccountId>,
         winning_target_count: u64,
+        cycle_id: u64,
         expected_result: Result<(), ()>,
     ) -> () {
         let extra_winning_target_count = winning_target_count - 1;
@@ -392,15 +391,17 @@ impl InstanceMocks<Runtime, Instance0> {
             Module::<Runtime, Instance0>::start_referendum(
                 InstanceMockUtils::<Runtime, Instance0>::mock_origin(origin),
                 extra_winning_target_count,
+                cycle_id,
             ),
             expected_result,
         );
 
-        Self::start_referendum_inner(extra_winning_target_count, expected_result)
+        Self::start_referendum_inner(extra_winning_target_count, cycle_id, expected_result)
     }
 
     pub fn start_referendum_manager(
         winning_target_count: u64,
+        cycle_id: u64,
         expected_result: Result<(), ()>,
     ) -> () {
         let extra_winning_target_count = winning_target_count - 1;
@@ -414,15 +415,20 @@ impl InstanceMocks<Runtime, Instance0> {
             >>::start_referendum(
                 InstanceMockUtils::<Runtime, Instance0>::mock_origin(OriginType::Root),
                 extra_winning_target_count,
+                cycle_id,
             )
             .is_ok(),
             expected_result.is_ok(),
         );
 
-        Self::start_referendum_inner(extra_winning_target_count, expected_result)
+        Self::start_referendum_inner(extra_winning_target_count, cycle_id, expected_result)
     }
 
-    fn start_referendum_inner(extra_winning_target_count: u64, expected_result: Result<(), ()>) {
+    fn start_referendum_inner(
+        extra_winning_target_count: u64,
+        cycle_id: u64,
+        expected_result: Result<(), ()>,
+    ) {
         if expected_result.is_err() {
             return;
         }
@@ -435,6 +441,7 @@ impl InstanceMocks<Runtime, Instance0> {
             ReferendumStage::Voting(ReferendumStageVoting {
                 started: block_number + 1, // actual voting starts in the next block (thats why +1)
                 winning_target_count,
+                current_cycle_id: cycle_id,
             }),
         );
 
@@ -446,7 +453,7 @@ impl InstanceMocks<Runtime, Instance0> {
         );
     }
 
-    pub fn check_voting_finished(winning_target_count: u64) {
+    pub fn check_voting_finished(winning_target_count: u64, cycle_id: u64) {
         let block_number = system::Module::<Runtime>::block_number();
 
         assert_eq!(
@@ -455,6 +462,7 @@ impl InstanceMocks<Runtime, Instance0> {
                 started: block_number,
                 winning_target_count,
                 intermediate_winners: vec![],
+                current_cycle_id: cycle_id,
             }),
         );
 
@@ -488,6 +496,7 @@ impl InstanceMocks<Runtime, Instance0> {
         account_id: <Runtime as system::Trait>::AccountId,
         commitment: <Runtime as system::Trait>::Hash,
         stake: Balance<Runtime, Instance0>,
+        cycle_id: u64,
         expected_result: Result<(), Error<Runtime, Instance0>>,
     ) -> () {
         // check method returns expected result
@@ -508,7 +517,7 @@ impl InstanceMocks<Runtime, Instance0> {
             Votes::<Runtime, Instance0>::get(account_id),
             CastVote {
                 commitment,
-                cycle_id: CurrentCycleId::<Instance0>::get(),
+                cycle_id: cycle_id.clone(),
                 stake,
                 vote_for: None,
             },
@@ -556,10 +565,9 @@ impl InstanceMocks<Runtime, Instance0> {
     ) -> () {
         // check method returns expected result
         assert_eq!(
-            Module::<Runtime, Instance0>::release_voting_stake(InstanceMockUtils::<
-                Runtime,
-                Instance0,
-            >::mock_origin(origin),),
+            Module::<Runtime, Instance0>::release_vote_stake(
+                InstanceMockUtils::<Runtime, Instance0>::mock_origin(origin),
+            ),
             expected_result,
         );
 

+ 265 - 65
runtime-modules/referendum/src/tests.rs

@@ -13,8 +13,9 @@ type MockUtils = InstanceMockUtils<Runtime, Instance0>;
 fn referendum_start() {
     MockUtils::origin_access(USER_ADMIN, |origin| {
         let winning_target_count = 1;
+        let cycle_id = 1;
 
-        Mocks::start_referendum_extrinsic(origin, winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(origin, winning_target_count, cycle_id, Ok(()));
     });
 }
 
@@ -25,8 +26,14 @@ fn referendum_start_access_restricted() {
 
     build_test_externalities(config).execute_with(|| {
         let winning_target_count = 1;
+        let cycle_id = 1;
 
-        Mocks::start_referendum_extrinsic(OriginType::None, winning_target_count, Err(()));
+        Mocks::start_referendum_extrinsic(
+            OriginType::None,
+            winning_target_count,
+            cycle_id,
+            Err(()),
+        );
     });
 }
 
@@ -39,9 +46,15 @@ fn referendum_start_forbidden_after_start() {
         let origin = OriginType::Signed(USER_ADMIN);
         let options = 1;
         let winning_target_count = 1;
+        let cycle_id = 1;
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
-        Mocks::start_referendum_extrinsic(origin.clone(), options.clone(), Err(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
+        Mocks::start_referendum_extrinsic(origin.clone(), options.clone(), cycle_id, Err(()));
     });
 }
 
@@ -55,15 +68,29 @@ fn voting() {
     build_test_externalities(config).execute_with(|| {
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
 
         let winning_target_count = 1;
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, _) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, _) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count,
+            cycle_id.clone(),
+            Ok(()),
+        );
 
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
     });
 }
 
@@ -75,11 +102,13 @@ fn voting_referendum_not_running() {
     build_test_externalities(config).execute_with(|| {
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
 
         let winning_target_count = 1;
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, _) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, _) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
         // try to vote before referendum starts
         Mocks::vote(
@@ -87,10 +116,16 @@ fn voting_referendum_not_running() {
             account_id,
             commitment,
             stake,
+            cycle_id.clone(),
             Err(Error::ReferendumNotRunning),
         );
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count,
+            cycle_id.clone(),
+            Ok(()),
+        );
 
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
         MockUtils::increase_block_number(voting_stage_duration + 1);
@@ -101,6 +136,7 @@ fn voting_referendum_not_running() {
             account_id,
             commitment,
             stake,
+            cycle_id.clone(),
             Err(Error::ReferendumNotRunning),
         );
     });
@@ -114,18 +150,26 @@ fn voting_stake_too_low() {
     build_test_externalities(config).execute_with(|| {
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
 
         let winning_target_count = 1;
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get() - 1;
-        let (commitment, _) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, _) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count,
+            cycle_id.clone(),
+            Ok(()),
+        );
         Mocks::vote(
             origin.clone(),
             account_id,
             commitment,
             stake,
+            cycle_id.clone(),
             Err(Error::InsufficientStake),
         );
     });
@@ -139,19 +183,27 @@ fn voting_user_repeated_vote() {
     build_test_externalities(config).execute_with(|| {
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
 
         let winning_target_count = 1;
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
         let different_stake = stake * 2;
-        let (commitment, _) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, _) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count,
+            cycle_id.clone(),
+            Ok(()),
+        );
         Mocks::vote(
             origin.clone(),
             account_id,
             commitment,
             stake.clone(),
+            cycle_id.clone(),
             Ok(()),
         );
 
@@ -160,6 +212,7 @@ fn voting_user_repeated_vote() {
             account_id,
             commitment,
             different_stake.clone(),
+            cycle_id.clone(),
             Err(Error::AlreadyVotedThisCycle),
         );
     });
@@ -172,14 +225,20 @@ fn voting_user_repeated_vote() {
 fn finish_voting() {
     MockUtils::origin_access(USER_ADMIN, |origin| {
         let winning_target_count = 1;
+        let cycle_id = 1;
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count,
+            cycle_id.clone(),
+            Ok(()),
+        );
 
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
 
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id.clone());
     });
 }
 
@@ -194,17 +253,31 @@ fn reveal() {
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let option_to_vote_for = 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, salt) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, salt) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin.clone(),
             account_id,
@@ -225,13 +298,20 @@ fn reveal_reveal_stage_not_running() {
         let reveal_stage_duration = <Runtime as Trait<Instance0>>::RevealStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let option_to_vote_for = 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, salt) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, salt) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
 
         Mocks::reveal_vote(
             origin.clone(),
@@ -241,10 +321,17 @@ fn reveal_reveal_stage_not_running() {
             Err(Error::RevealingNotInProgress),
         );
 
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         MockUtils::increase_block_number(reveal_stage_duration);
         Mocks::check_revealing_finished(vec![], MockUtils::transform_results(vec![]));
 
@@ -268,12 +355,18 @@ fn reveal_no_vote() {
         let reveal_stage_duration = <Runtime as Trait<Instance0>>::RevealStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         MockUtils::increase_block_number(reveal_stage_duration);
 
         Mocks::check_revealing_finished(vec![], MockUtils::transform_results(vec![]));
@@ -290,6 +383,7 @@ fn reveal_salt_too_long() {
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let mut salt = vec![];
@@ -299,14 +393,30 @@ fn reveal_salt_too_long() {
 
         let option_to_vote_for = 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, _) =
-            MockUtils::calculate_commitment_custom_salt(&account_id, &option_to_vote_for, &salt);
+        let (commitment, _) = MockUtils::calculate_commitment_custom_salt(
+            &account_id,
+            &option_to_vote_for,
+            &salt,
+            &cycle_id,
+        );
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin.clone(),
             account_id,
@@ -326,20 +436,34 @@ fn reveal_invalid_vote() {
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let invalid_option = 1000;
         let option_to_vote_for = 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, salt) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, salt) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
         Runtime::feature_option_id_valid(false);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin.clone(),
             account_id,
@@ -359,18 +483,32 @@ fn reveal_invalid_commitment_proof() {
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let option_to_vote_for = 0;
         let invalid_option = option_to_vote_for + 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, salt) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, salt) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin.clone(),
             account_id,
@@ -393,17 +531,31 @@ fn finish_revealing_period() {
         let reveal_stage_duration = <Runtime as Trait<Instance0>>::RevealStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, salt) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, salt) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
-        Mocks::vote(origin.clone(), account_id, commitment, stake, Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
+        Mocks::vote(
+            origin.clone(),
+            account_id,
+            commitment,
+            stake,
+            cycle_id.clone(),
+            Ok(()),
+        );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin.clone(),
             account_id,
@@ -437,6 +589,7 @@ fn finish_revealing_period_vote_power() {
         let origin = OriginType::Signed(account_superuser);
         let origin_voter1 = OriginType::Signed(account_id1);
         let origin_voter2 = OriginType::Signed(account_id2);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let option_to_vote_for1 = 0;
@@ -444,16 +597,22 @@ fn finish_revealing_period_vote_power() {
         let stake_bigger = <Runtime as Trait<Instance0>>::MinimumStake::get() * 2;
         let stake_smaller = <Runtime as Trait<Instance0>>::MinimumStake::get();
         let (commitment1, salt1) =
-            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for1);
+            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for1, &cycle_id);
         let (commitment2, salt2) =
-            MockUtils::calculate_commitment(&account_id2, &option_to_vote_for2);
+            MockUtils::calculate_commitment(&account_id2, &option_to_vote_for2, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
         Mocks::vote(
             origin_voter1.clone(),
             account_id1,
             commitment1,
             stake_bigger,
+            cycle_id.clone(),
             Ok(()),
         ); // vote for first option by regular user
         Mocks::vote(
@@ -461,11 +620,12 @@ fn finish_revealing_period_vote_power() {
             account_id2,
             commitment2,
             stake_smaller,
+            cycle_id.clone(),
             Ok(()),
         ); // vote for second option by prominent user
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin_voter1.clone(),
             account_id1,
@@ -508,11 +668,12 @@ fn winners_no_vote_revealed() {
         let voting_stage_duration = <Runtime as Trait<Instance0>>::VoteStageDuration::get();
         let reveal_stage_duration = <Runtime as Trait<Instance0>>::RevealStageDuration::get();
         let origin = OriginType::Signed(USER_ADMIN);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, Ok(()));
+        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count, cycle_id, Ok(()));
         MockUtils::increase_block_number(voting_stage_duration);
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         MockUtils::increase_block_number(reveal_stage_duration);
         Mocks::check_revealing_finished(vec![], MockUtils::transform_results(vec![]));
     });
@@ -534,24 +695,31 @@ fn winners_multiple_winners() {
         let origin_voter1 = OriginType::Signed(account_id1);
         let origin_voter2 = OriginType::Signed(account_id2);
         let origin_voter3 = OriginType::Signed(account_id3);
+        let cycle_id = 1;
         let winning_target_count = 2;
 
         let option_to_vote_for1 = 0;
         let option_to_vote_for2 = 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
         let (commitment1, salt1) =
-            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for1);
+            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for1, &cycle_id);
         let (commitment2, salt2) =
-            MockUtils::calculate_commitment(&account_id2, &option_to_vote_for1);
+            MockUtils::calculate_commitment(&account_id2, &option_to_vote_for1, &cycle_id);
         let (commitment3, salt3) =
-            MockUtils::calculate_commitment(&account_id3, &option_to_vote_for2);
+            MockUtils::calculate_commitment(&account_id3, &option_to_vote_for2, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
         Mocks::vote(
             origin_voter1.clone(),
             account_id1,
             commitment1,
             stake,
+            cycle_id.clone(),
             Ok(()),
         );
         Mocks::vote(
@@ -559,6 +727,7 @@ fn winners_multiple_winners() {
             account_id2,
             commitment2,
             stake,
+            cycle_id.clone(),
             Ok(()),
         );
         Mocks::vote(
@@ -566,11 +735,12 @@ fn winners_multiple_winners() {
             account_id3,
             commitment3,
             stake,
+            cycle_id.clone(),
             Ok(()),
         );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
 
         Mocks::reveal_vote(
             origin_voter1.clone(),
@@ -626,22 +796,29 @@ fn winners_multiple_winners_extra() {
         let origin = OriginType::Signed(account_superuser);
         let origin_voter1 = OriginType::Signed(account_id1);
         let origin_voter2 = OriginType::Signed(account_id2);
+        let cycle_id = 1;
         let winning_target_count = 1;
 
         let option_to_vote_for1 = 0;
         let option_to_vote_for2 = 1;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
         let (commitment1, salt1) =
-            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for1);
+            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for1, &cycle_id);
         let (commitment2, salt2) =
-            MockUtils::calculate_commitment(&account_id2, &option_to_vote_for2);
+            MockUtils::calculate_commitment(&account_id2, &option_to_vote_for2, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
         Mocks::vote(
             origin_voter1.clone(),
             account_id1,
             commitment1,
             stake,
+            cycle_id.clone(),
             Ok(()),
         );
         Mocks::vote(
@@ -649,11 +826,12 @@ fn winners_multiple_winners_extra() {
             account_id2,
             commitment2,
             stake,
+            cycle_id.clone(),
             Ok(()),
         );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin_voter1.clone(),
             account_id1,
@@ -694,24 +872,31 @@ fn winners_multiple_not_enough() {
         let account_id1 = USER_REGULAR;
         let origin = OriginType::Signed(account_superuser);
         let origin_voter1 = OriginType::Signed(account_id1);
+        let cycle_id = 1;
         let winning_target_count = 3;
 
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
         let (commitment1, salt1) =
-            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for);
+            MockUtils::calculate_commitment(&account_id1, &option_to_vote_for, &cycle_id);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id,
+            Ok(()),
+        );
         Mocks::vote(
             origin_voter1.clone(),
             account_id1,
             commitment1,
             stake,
+            cycle_id.clone(),
             Ok(()),
         );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id);
         Mocks::reveal_vote(
             origin_voter1.clone(),
             account_id1,
@@ -745,23 +930,32 @@ fn referendum_release_stake() {
         let reveal_stage_duration = <Runtime as Trait<Instance0>>::RevealStageDuration::get();
         let account_id = USER_ADMIN;
         let origin = OriginType::Signed(account_id);
+        let cycle_id1 = 1;
+        let cycle_id2 = 2;
         let winning_target_count = 1;
 
         let option_to_vote_for = 0;
         let stake = <Runtime as Trait<Instance0>>::MinimumStake::get();
-        let (commitment, salt) = MockUtils::calculate_commitment(&account_id, &option_to_vote_for);
+        let (commitment, salt) =
+            MockUtils::calculate_commitment(&account_id, &option_to_vote_for, &cycle_id1);
 
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id1,
+            Ok(()),
+        );
         Mocks::vote(
             origin.clone(),
             account_id,
             commitment,
             stake.clone(),
+            cycle_id1.clone(),
             Ok(()),
         );
         MockUtils::increase_block_number(voting_stage_duration);
 
-        Mocks::check_voting_finished(winning_target_count);
+        Mocks::check_voting_finished(winning_target_count, cycle_id1);
         Mocks::reveal_vote(
             origin.clone(),
             account_id,
@@ -784,7 +978,12 @@ fn referendum_release_stake() {
         Runtime::feature_stack_lock(true);
 
         // since `account_id` voted for the winner, he can unlock stake only after inactive stage ends
-        Mocks::start_referendum_extrinsic(origin.clone(), winning_target_count.clone(), Ok(()));
+        Mocks::start_referendum_extrinsic(
+            origin.clone(),
+            winning_target_count.clone(),
+            cycle_id2,
+            Ok(()),
+        );
 
         Mocks::release_stake(origin.clone(), account_id, Ok(()));
     });
@@ -799,7 +998,8 @@ fn referendum_manager_referendum_start() {
 
     build_test_externalities(config).execute_with(|| {
         let winning_target_count = 1;
+        let cycle_id = 1;
 
-        Mocks::start_referendum_manager(winning_target_count, Ok(()));
+        Mocks::start_referendum_manager(winning_target_count, cycle_id, Ok(()));
     });
 }