123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857 |
- #![cfg_attr(not(feature = "std"), no_std)]
- use codec::{Codec, Decode, Encode};
- use frame_support::traits::{
- Currency, EnsureOrigin, Get, LockIdentifier, LockableCurrency, WithdrawReason,
- };
- use frame_support::{
- decl_error, decl_event, decl_module, decl_storage, error::BadOrigin, Parameter, StorageValue,
- };
- use sp_arithmetic::traits::BaseArithmetic;
- use sp_runtime::traits::{MaybeSerialize, Member};
- use std::marker::PhantomData;
- use system::ensure_signed;
- mod mock;
- mod tests;
- #[derive(Encode, Decode, PartialEq, Eq, Debug)]
- pub enum ReferendumStage<BlockNumber, VotePower> {
-
- Inactive,
-
- Voting(ReferendumStageVoting<BlockNumber>),
-
- Revealing(ReferendumStageRevealing<BlockNumber, VotePower>),
- }
- impl<BlockNumber, VotePower: Encode + Decode> Default for ReferendumStage<BlockNumber, VotePower> {
- fn default() -> ReferendumStage<BlockNumber, VotePower> {
- ReferendumStage::Inactive
- }
- }
- #[derive(Encode, Decode, PartialEq, Eq, Debug, Default)]
- pub struct ReferendumStageVoting<BlockNumber> {
- pub started: BlockNumber,
- pub winning_target_count: u64,
- }
- #[derive(Encode, Decode, PartialEq, Eq, Debug, Default)]
- pub struct ReferendumStageRevealing<BlockNumber, VotePower> {
- pub started: BlockNumber,
- pub winning_target_count: u64,
- pub intermediate_winners: Vec<OptionResult<VotePower>>,
- }
- #[derive(Encode, Decode, PartialEq, Eq, Debug, Default, Clone)]
- pub struct OptionResult<VotePower> {
- pub option_id: u64,
- pub vote_power: VotePower,
- }
- #[derive(Encode, Decode, PartialEq, Eq, Debug, Default)]
- pub struct CastVote<Hash, Currency> {
- pub commitment: Hash,
- pub cycle_id: u64,
- pub stake: Currency,
- pub vote_for: Option<u64>,
- }
- pub type Balance<T, I> =
- <<T as Trait<I>>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
- pub type CastVoteOf<T, I> = CastVote<<T as system::Trait>::Hash, Balance<T, I>>;
- pub type ReferendumStageVotingOf<T> = ReferendumStageVoting<<T as system::Trait>::BlockNumber>;
- pub type ReferendumStageRevealingOf<T, I> =
- ReferendumStageRevealing<<T as system::Trait>::BlockNumber, <T as Trait<I>>::VotePower>;
- pub type CanRevealResult<T, I> = (
- ReferendumStageRevealingOf<T, I>,
- <T as system::Trait>::AccountId,
- CastVoteOf<T, I>,
- );
- pub trait ReferendumManager<Origin, AccountId, Hash> {
-
- type VotePower: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + MaybeSerialize
- + PartialEq;
-
- type Currency: LockableCurrency<AccountId>;
-
- fn start_referendum(origin: Origin, extra_winning_target_count: u64) -> Result<(), ()>;
-
-
- fn force_start(extra_winning_target_count: u64);
-
- fn calculate_commitment(
- account_id: &AccountId,
- salt: &[u8],
- cycle_id: &u64,
- vote_option_id: &u64,
- ) -> Hash;
- }
- pub trait Trait<I: Instance>: system::Trait {
-
- type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
-
- type MaxSaltLength: Get<u64>;
-
- type Currency: LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>;
-
- type LockId: Get<LockIdentifier>;
-
- type ManagerOrigin: EnsureOrigin<Self::Origin>;
-
- type VotePower: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + MaybeSerialize
- + PartialEq;
-
- type VoteStageDuration: Get<Self::BlockNumber>;
-
- type RevealStageDuration: Get<Self::BlockNumber>;
-
- type MinimumStake: Get<Balance<Self, I>>;
-
- fn calculate_vote_power(
- account_id: &<Self as system::Trait>::AccountId,
- stake: &Balance<Self, I>,
- ) -> <Self as Trait<I>>::VotePower;
-
-
- fn can_release_vote_stake(
- vote: &CastVote<Self::Hash, Balance<Self, I>>,
- current_voting_cycle_id: &u64,
- ) -> bool;
-
- fn process_results(winners: &[OptionResult<Self::VotePower>]);
-
- fn is_valid_option_id(option_id: &u64) -> bool;
-
- fn get_option_power(option_id: &u64) -> Self::VotePower;
-
- fn increase_option_power(option_id: &u64, amount: &Self::VotePower);
- }
- decl_storage! {
- trait Store for Module<T: Trait<I>, I: Instance> as Referendum {
-
- pub Stage get(fn stage) config(): ReferendumStage<T::BlockNumber, T::VotePower>;
-
-
-
-
- pub Votes get(fn votes) config(): map hasher(blake2_128_concat) T::AccountId => CastVoteOf<T, I>;
-
- pub CurrentCycleId get(fn current_cycle_id) config(): u64;
- }
- }
- decl_event! {
- pub enum Event<T, I>
- where
- Balance = Balance<T, I>,
- <T as system::Trait>::Hash,
- <T as system::Trait>::AccountId,
- <T as Trait<I>>::VotePower,
- {
-
- ReferendumStarted(u64),
-
- ReferendumStartedForcefully(u64),
-
- RevealingStageStarted(),
-
- ReferendumFinished(Vec<OptionResult<VotePower>>),
-
- VoteCast(AccountId, Hash, Balance),
-
- VoteRevealed(AccountId, u64),
-
- StakeReleased(AccountId),
- }
- }
- decl_error! {
-
- pub enum Error for Module<T: Trait<I>, I: Instance> {
-
- BadOrigin,
-
- ReferendumNotRunning,
-
- RevealingNotInProgress,
-
- InsufficientBalanceToStakeCurrency,
-
- InsufficientStake,
-
- InvalidReveal,
-
- InvalidVote,
-
- VoteNotExisting,
-
- AlreadyVotedThisCycle,
-
- UnstakingVoteInSameCycle,
-
- SaltTooLong,
-
- UnstakingForbidden,
- }
- }
- impl<T: Trait<I>, I: Instance> PartialEq for Error<T, I> {
- fn eq(&self, other: &Self) -> bool {
- self.as_u8() == other.as_u8()
- }
- }
- impl<T: Trait<I>, I: Instance> From<BadOrigin> for Error<T, I> {
- fn from(_error: BadOrigin) -> Self {
- Error::<T, I>::BadOrigin
- }
- }
- decl_module! {
- pub struct Module<T: Trait<I>, I: Instance> for enum Call where origin: T::Origin {
-
- type Error = Error<T, I>;
-
- fn deposit_event() = default;
-
-
- fn on_finalize(now: T::BlockNumber) {
- Self::try_progress_stage(now);
- }
-
-
- #[weight = 10_000_000]
- pub fn vote(origin, commitment: T::Hash, stake: Balance<T, I>) -> Result<(), Error<T, I>> {
-
- let account_id = EnsureChecks::<T, I>::can_vote(origin, &stake)?;
-
-
-
-
- Mutations::<T, I>::vote(&account_id, &commitment, &stake)?;
-
- Self::deposit_event(RawEvent::VoteCast(account_id, commitment, stake));
- Ok(())
- }
-
- #[weight = 10_000_000]
- pub fn reveal_vote(origin, salt: Vec<u8>, vote_option_id: u64) -> Result<(), Error<T, I>> {
- let (stage_data, account_id, cast_vote) = EnsureChecks::<T, I>::can_reveal_vote::<Self>(origin, &salt, &vote_option_id)?;
-
-
-
-
- Mutations::<T, I>::reveal_vote(stage_data, &account_id, &vote_option_id, cast_vote)?;
-
- Self::deposit_event(RawEvent::VoteRevealed(account_id, vote_option_id));
- Ok(())
- }
-
- #[weight = 10_000_000]
- pub fn release_vote_stake(origin) -> Result<(), Error<T, I>> {
- let account_id = EnsureChecks::<T, I>::can_release_vote_stake(origin)?;
-
-
-
-
- Mutations::<T, I>::release_vote_stake(&account_id);
-
- Self::deposit_event(RawEvent::StakeReleased(account_id));
- Ok(())
- }
- }
- }
- impl<T: Trait<I>, I: Instance> Module<T, I> {
-
- fn try_progress_stage(now: T::BlockNumber) {
- match Stage::<T, I>::get() {
- ReferendumStage::Inactive => (),
- ReferendumStage::Voting(stage_data) => {
- if now == stage_data.started + T::VoteStageDuration::get() - 1.into() {
- Self::end_voting_period(stage_data);
- }
- }
- ReferendumStage::Revealing(stage_data) => {
- if now == stage_data.started + T::RevealStageDuration::get() - 1.into() {
- Self::end_reveal_period(stage_data);
- }
- }
- }
- }
-
- fn end_voting_period(stage_data: ReferendumStageVotingOf<T>) {
-
- Mutations::<T, I>::start_revealing_period(stage_data);
-
- Self::deposit_event(RawEvent::RevealingStageStarted());
- }
-
- fn end_reveal_period(stage_data: ReferendumStageRevealingOf<T, I>) {
-
- let winners = Mutations::<T, I>::conclude_referendum(stage_data);
-
- T::process_results(&winners);
-
- Self::deposit_event(RawEvent::ReferendumFinished(winners));
- }
- }
- impl<T: Trait<I>, I: Instance> ReferendumManager<T::Origin, T::AccountId, T::Hash>
- for Module<T, I>
- {
- type VotePower = T::VotePower;
- type Currency = T::Currency;
-
- fn start_referendum(origin: T::Origin, extra_winning_target_count: u64) -> Result<(), ()> {
- let winning_target_count = extra_winning_target_count + 1;
-
- EnsureChecks::<T, I>::can_start_referendum(origin)?;
-
-
-
-
- Mutations::<T, I>::start_voting_period(&winning_target_count);
-
- Self::deposit_event(RawEvent::ReferendumStarted(winning_target_count));
- Ok(())
- }
-
-
- fn force_start(extra_winning_target_count: u64) {
- let winning_target_count = extra_winning_target_count + 1;
-
- let referendum_running = !matches!(Stage::<T, I>::get(), ReferendumStage::Inactive);
-
- Mutations::<T, I>::start_voting_period(&winning_target_count);
-
- if referendum_running {
- Self::deposit_event(RawEvent::ReferendumStartedForcefully(winning_target_count));
- } else {
- Self::deposit_event(RawEvent::ReferendumStarted(winning_target_count));
- }
- }
-
- fn calculate_commitment(
- account_id: &<T as system::Trait>::AccountId,
- salt: &[u8],
- cycle_id: &u64,
- vote_option_id: &u64,
- ) -> T::Hash {
- let mut payload = account_id.encode();
- let mut mut_option_id = vote_option_id.encode();
- let mut mut_salt = salt.encode();
- let mut mut_cycle_id = cycle_id.encode();
- payload.append(&mut mut_option_id);
- payload.append(&mut mut_salt);
- payload.append(&mut mut_cycle_id);
- <T::Hashing as sp_runtime::traits::Hash>::hash(&payload)
- }
- }
- struct Mutations<T: Trait<I>, I: Instance> {
- _dummy: PhantomData<(T, I)>,
- }
- impl<T: Trait<I>, I: Instance> Mutations<T, I> {
-
- fn start_voting_period(winning_target_count: &u64) {
-
- Stage::<T, I>::put(ReferendumStage::Voting(ReferendumStageVoting::<
- T::BlockNumber,
- > {
- started: <system::Module<T>>::block_number() + 1.into(),
- winning_target_count: *winning_target_count,
- }));
- }
-
- fn start_revealing_period(old_stage: ReferendumStageVotingOf<T>) {
-
- Stage::<T, I>::put(ReferendumStage::Revealing(ReferendumStageRevealingOf::<
- T,
- I,
- > {
- started: <system::Module<T>>::block_number() + 1.into(),
- winning_target_count: old_stage.winning_target_count,
- intermediate_winners: vec![],
- }));
- }
-
- fn conclude_referendum(
- revealing_stage: ReferendumStageRevealingOf<T, I>,
- ) -> Vec<OptionResult<<T as Trait<I>>::VotePower>> {
-
- Self::reset_referendum();
-
- revealing_stage.intermediate_winners
- }
-
- fn reset_referendum() {
- Stage::<T, I>::put(ReferendumStage::Inactive);
- CurrentCycleId::<I>::put(CurrentCycleId::<I>::get() + 1);
- }
-
- fn vote(
- account_id: &<T as system::Trait>::AccountId,
- commitment: &T::Hash,
- stake: &Balance<T, I>,
- ) -> Result<(), Error<T, I>> {
-
- T::Currency::set_lock(
- T::LockId::get(),
- account_id,
- *stake,
- WithdrawReason::Transfer.into(),
- );
-
- Votes::<T, I>::insert(
- account_id,
- CastVote {
- commitment: *commitment,
- stake: *stake,
- cycle_id: CurrentCycleId::<I>::get(),
- vote_for: None,
- },
- );
- Ok(())
- }
-
- fn reveal_vote(
- stage_data: ReferendumStageRevealingOf<T, I>,
- account_id: &<T as system::Trait>::AccountId,
- option_id: &u64,
- cast_vote: CastVoteOf<T, I>,
- ) -> Result<(), Error<T, I>> {
-
- let vote_power = T::calculate_vote_power(&account_id, &cast_vote.stake);
- let option_result = OptionResult {
- option_id: *option_id,
- vote_power,
- };
-
- let new_winners = Self::try_winner_insert(
- option_result,
- &stage_data.intermediate_winners,
- stage_data.winning_target_count,
- );
- let new_stage_data = ReferendumStageRevealing {
- intermediate_winners: new_winners,
- ..stage_data
- };
-
- T::increase_option_power(option_id, &vote_power);
-
- Stage::<T, I>::mutate(|stage| *stage = ReferendumStage::Revealing(new_stage_data));
-
- Votes::<T, I>::mutate(account_id, |vote| (*vote).vote_for = Some(*option_id));
- Ok(())
- }
-
- fn release_vote_stake(account_id: &<T as system::Trait>::AccountId) {
-
- T::Currency::remove_lock(T::LockId::get(), account_id);
-
- Votes::<T, I>::remove(account_id);
- }
-
- fn try_winner_insert(
- option_result: OptionResult<T::VotePower>,
- current_winners: &[OptionResult<T::VotePower>],
- winning_target_count: u64,
- ) -> Vec<OptionResult<T::VotePower>> {
-
- fn place_record_to_winner_list<T: Trait<I>, I: Instance>(
- option_result: OptionResult<T::VotePower>,
- current_winners: &[OptionResult<T::VotePower>],
- winning_target_count: u64,
- ) -> (Vec<OptionResult<T::VotePower>>, Option<usize>) {
- let current_winners_count = current_winners.len();
-
- let current_winners_index_of_vote_recipient: Option<usize> = current_winners
- .iter()
- .enumerate()
- .find(|(_, value)| option_result.option_id == value.option_id)
- .map(|(index, _)| index);
-
- if current_winners_index_of_vote_recipient.is_none()
- && current_winners_count as u64 == winning_target_count
- && option_result.vote_power <= current_winners[current_winners_count - 1].vote_power
- {
- return (current_winners.to_vec(), None);
- }
- let mut new_winners = current_winners.to_vec();
-
- if let Some(index) = current_winners_index_of_vote_recipient {
- let old_option_total = T::get_option_power(&option_result.option_id);
- let new_option_total = old_option_total + option_result.vote_power;
- new_winners[index] = OptionResult {
- option_id: option_result.option_id,
- vote_power: new_option_total,
- };
- return (new_winners, Some(index));
- }
-
-
- if current_winners_count as u64 == winning_target_count {
- let last_index = current_winners_count - 1;
- new_winners[last_index] = option_result;
- return (new_winners, Some(last_index));
- }
-
- new_winners.push(option_result);
- (new_winners, Some(current_winners_count))
- }
-
- if current_winners.is_empty() {
- return vec![option_result];
- }
-
- let (mut new_winners, current_record_index) = place_record_to_winner_list::<T, I>(
- option_result,
- current_winners,
- winning_target_count,
- );
-
- if let Some(index) = current_record_index {
- for i in (1..=index).rev() {
- if new_winners[i].vote_power <= new_winners[i - 1].vote_power {
- break;
- }
- new_winners.swap(i, i - 1);
- }
- }
- new_winners.to_vec()
- }
- }
- struct EnsureChecks<T: Trait<I>, I: Instance> {
- _dummy: PhantomData<(T, I)>,
- }
- impl<T: Trait<I>, I: Instance> EnsureChecks<T, I> {
-
- fn ensure_regular_user(origin: T::Origin) -> Result<T::AccountId, Error<T, I>> {
- let account_id = ensure_signed(origin)?;
- Ok(account_id)
- }
-
- fn can_start_referendum(origin: T::Origin) -> Result<(), ()> {
- T::ManagerOrigin::ensure_origin(origin).map_err(|_| ())?;
-
- match Stage::<T, I>::get() {
- ReferendumStage::Inactive => Ok(()),
- _ => Err(()),
- }?;
- Ok(())
- }
- fn can_vote(origin: T::Origin, stake: &Balance<T, I>) -> Result<T::AccountId, Error<T, I>> {
- fn prevent_repeated_vote<T: Trait<I>, I: Instance>(
- account_id: &T::AccountId,
- ) -> Result<(), Error<T, I>> {
- if !Votes::<T, I>::contains_key(&account_id) {
- return Ok(());
- }
- let existing_vote = Votes::<T, I>::get(&account_id);
-
- if existing_vote.cycle_id == CurrentCycleId::<I>::get() {
- return Err(Error::<T, I>::AlreadyVotedThisCycle);
- }
- Ok(())
- }
-
- let account_id = Self::ensure_regular_user(origin)?;
- let stage = Stage::<T, I>::get();
-
- match stage {
- ReferendumStage::Voting(_) => (),
- _ => return Err(Error::ReferendumNotRunning),
- };
-
- prevent_repeated_vote::<T, I>(&account_id)?;
-
- if stake < &T::MinimumStake::get() {
- return Err(Error::InsufficientStake);
- }
-
- if T::Currency::total_balance(&account_id) < *stake {
- return Err(Error::InsufficientBalanceToStakeCurrency);
- }
- Ok(account_id)
- }
- fn can_reveal_vote<R: ReferendumManager<T::Origin, T::AccountId, T::Hash>>(
- origin: T::Origin,
- salt: &[u8],
- vote_option_id: &u64,
- ) -> Result<CanRevealResult<T, I>, Error<T, I>> {
- let cycle_id = CurrentCycleId::<I>::get();
-
- let account_id = Self::ensure_regular_user(origin)?;
- let stage = Stage::<T, I>::get();
-
- let stage_data = match stage {
- ReferendumStage::Revealing(tmp_stage_data) => tmp_stage_data,
- _ => return Err(Error::RevealingNotInProgress),
- };
- let cast_vote = Self::ensure_vote_exists(&account_id)?;
-
- if !T::is_valid_option_id(vote_option_id) {
- return Err(Error::InvalidVote);
- }
-
- if cycle_id != cast_vote.cycle_id {
- return Err(Error::InvalidVote);
- }
-
- if salt.len() as u64 > T::MaxSaltLength::get() {
- return Err(Error::SaltTooLong);
- }
-
- let commitment = R::calculate_commitment(&account_id, salt, &cycle_id, vote_option_id);
- if commitment != cast_vote.commitment {
- return Err(Error::InvalidReveal);
- }
- Ok((stage_data, account_id, cast_vote))
- }
- fn can_release_vote_stake(origin: T::Origin) -> Result<T::AccountId, Error<T, I>> {
- let cycle_id = CurrentCycleId::<I>::get();
-
- let account_id = Self::ensure_regular_user(origin)?;
- let cast_vote = Self::ensure_vote_exists(&account_id)?;
-
- if cycle_id == cast_vote.cycle_id {
- return Err(Error::UnstakingVoteInSameCycle);
- }
-
- if !T::can_release_vote_stake(&cast_vote, &cycle_id) {
- return Err(Error::UnstakingForbidden);
- }
- Ok(account_id)
- }
- fn ensure_vote_exists(account_id: &T::AccountId) -> Result<CastVoteOf<T, I>, Error<T, I>> {
-
- if !Votes::<T, I>::contains_key(account_id) {
- return Err(Error::VoteNotExisting);
- }
- let cast_vote = Votes::<T, I>::get(account_id);
- Ok(cast_vote)
- }
- }
|