1#![recursion_limit = "256"]
153#![cfg_attr(not(feature = "std"), no_std)]
154
155extern crate alloc;
156
157use alloc::{vec, vec::Vec};
158use codec::{Decode, Encode};
159use frame_support::{
160 ensure,
161 traits::{
162 defensive_prelude::*,
163 schedule::{v3::Named as ScheduleNamed, DispatchTime},
164 Bounded, Currency, EnsureOrigin, Get, LockIdentifier, LockableCurrency, OnUnbalanced,
165 QueryPreimage, ReservableCurrency, StorePreimage, WithdrawReasons,
166 },
167 weights::Weight,
168};
169use frame_system::pallet_prelude::{BlockNumberFor, OriginFor};
170use sp_runtime::{
171 traits::{BadOrigin, Bounded as ArithBounded, One, Saturating, StaticLookup, Zero},
172 ArithmeticError, DispatchError, DispatchResult,
173};
174
175mod conviction;
176mod types;
177mod vote;
178mod vote_threshold;
179pub mod weights;
180pub use conviction::Conviction;
181pub use pallet::*;
182pub use types::{
183 Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus,
184 Tally, UnvoteScope,
185};
186pub use vote::{AccountVote, Vote, Voting};
187pub use vote_threshold::{Approved, VoteThreshold};
188pub use weights::WeightInfo;
189
190#[cfg(test)]
191mod tests;
192
193#[cfg(feature = "runtime-benchmarks")]
194pub mod benchmarking;
195
196pub mod migrations;
197
198pub(crate) const DEMOCRACY_ID: LockIdentifier = *b"democrac";
199
200type BalanceOf<T> =
201 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
202type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
203 <T as frame_system::Config>::AccountId,
204>>::NegativeImbalance;
205pub type CallOf<T> = <T as frame_system::Config>::RuntimeCall;
206pub type BoundedCallOf<T> = Bounded<CallOf<T>, <T as frame_system::Config>::Hashing>;
207type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
208
209#[frame_support::pallet]
210pub mod pallet {
211 use super::{DispatchResult, *};
212 use frame_support::pallet_prelude::*;
213 use frame_system::pallet_prelude::*;
214
215 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
217
218 #[pallet::pallet]
219 #[pallet::storage_version(STORAGE_VERSION)]
220 pub struct Pallet<T>(_);
221
222 #[pallet::config]
223 pub trait Config: frame_system::Config + Sized {
224 type WeightInfo: WeightInfo;
225 #[allow(deprecated)]
226 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
227
228 type Scheduler: ScheduleNamed<
230 BlockNumberFor<Self>,
231 CallOf<Self>,
232 Self::PalletsOrigin,
233 Hasher = Self::Hashing,
234 >;
235
236 type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
238
239 type Currency: ReservableCurrency<Self::AccountId>
241 + LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
242
243 #[pallet::constant]
249 type EnactmentPeriod: Get<BlockNumberFor<Self>>;
250
251 #[pallet::constant]
253 type LaunchPeriod: Get<BlockNumberFor<Self>>;
254
255 #[pallet::constant]
257 type VotingPeriod: Get<BlockNumberFor<Self>>;
258
259 #[pallet::constant]
264 type VoteLockingPeriod: Get<BlockNumberFor<Self>>;
265
266 #[pallet::constant]
268 type MinimumDeposit: Get<BalanceOf<Self>>;
269
270 #[pallet::constant]
274 type InstantAllowed: Get<bool>;
275
276 #[pallet::constant]
278 type FastTrackVotingPeriod: Get<BlockNumberFor<Self>>;
279
280 #[pallet::constant]
282 type CooloffPeriod: Get<BlockNumberFor<Self>>;
283
284 #[pallet::constant]
289 type MaxVotes: Get<u32>;
290
291 #[pallet::constant]
293 type MaxProposals: Get<u32>;
294
295 #[pallet::constant]
297 type MaxDeposits: Get<u32>;
298
299 #[pallet::constant]
301 type MaxBlacklisted: Get<u32>;
302
303 type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
306
307 type ExternalMajorityOrigin: EnsureOrigin<Self::RuntimeOrigin>;
310
311 type ExternalDefaultOrigin: EnsureOrigin<Self::RuntimeOrigin>;
314
315 type SubmitOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
319
320 type FastTrackOrigin: EnsureOrigin<Self::RuntimeOrigin>;
324
325 type InstantOrigin: EnsureOrigin<Self::RuntimeOrigin>;
329
330 type CancellationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
332
333 type BlacklistOrigin: EnsureOrigin<Self::RuntimeOrigin>;
335
336 type CancelProposalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
338
339 type VetoOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
341
342 type PalletsOrigin: From<frame_system::RawOrigin<Self::AccountId>>;
344
345 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
347 }
348
349 #[pallet::storage]
351 pub type PublicPropCount<T> = StorageValue<_, PropIndex, ValueQuery>;
352
353 #[pallet::storage]
355 pub type PublicProps<T: Config> = StorageValue<
356 _,
357 BoundedVec<(PropIndex, BoundedCallOf<T>, T::AccountId), T::MaxProposals>,
358 ValueQuery,
359 >;
360
361 #[pallet::storage]
365 pub type DepositOf<T: Config> = StorageMap<
366 _,
367 Twox64Concat,
368 PropIndex,
369 (BoundedVec<T::AccountId, T::MaxDeposits>, BalanceOf<T>),
370 >;
371
372 #[pallet::storage]
374 pub type ReferendumCount<T> = StorageValue<_, ReferendumIndex, ValueQuery>;
375
376 #[pallet::storage]
379 pub type LowestUnbaked<T> = StorageValue<_, ReferendumIndex, ValueQuery>;
380
381 #[pallet::storage]
385 pub type ReferendumInfoOf<T: Config> = StorageMap<
386 _,
387 Twox64Concat,
388 ReferendumIndex,
389 ReferendumInfo<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>,
390 >;
391
392 #[pallet::storage]
397 pub type VotingOf<T: Config> = StorageMap<
398 _,
399 Twox64Concat,
400 T::AccountId,
401 Voting<BalanceOf<T>, T::AccountId, BlockNumberFor<T>, T::MaxVotes>,
402 ValueQuery,
403 >;
404
405 #[pallet::storage]
408 pub type LastTabledWasExternal<T> = StorageValue<_, bool, ValueQuery>;
409
410 #[pallet::storage]
415 pub type NextExternal<T: Config> = StorageValue<_, (BoundedCallOf<T>, VoteThreshold)>;
416
417 #[pallet::storage]
420 pub type Blacklist<T: Config> = StorageMap<
421 _,
422 Identity,
423 T::Hash,
424 (BlockNumberFor<T>, BoundedVec<T::AccountId, T::MaxBlacklisted>),
425 >;
426
427 #[pallet::storage]
429 pub type Cancellations<T: Config> = StorageMap<_, Identity, T::Hash, bool, ValueQuery>;
430
431 #[pallet::storage]
438 pub type MetadataOf<T: Config> = StorageMap<_, Blake2_128Concat, MetadataOwner, T::Hash>;
439
440 #[pallet::genesis_config]
441 #[derive(frame_support::DefaultNoBound)]
442 pub struct GenesisConfig<T: Config> {
443 #[serde(skip)]
444 _config: core::marker::PhantomData<T>,
445 }
446
447 #[pallet::genesis_build]
448 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
449 fn build(&self) {
450 PublicPropCount::<T>::put(0 as PropIndex);
451 ReferendumCount::<T>::put(0 as ReferendumIndex);
452 LowestUnbaked::<T>::put(0 as ReferendumIndex);
453 }
454 }
455
456 #[pallet::event]
457 #[pallet::generate_deposit(pub(super) fn deposit_event)]
458 pub enum Event<T: Config> {
459 Proposed { proposal_index: PropIndex, deposit: BalanceOf<T> },
461 Tabled { proposal_index: PropIndex, deposit: BalanceOf<T> },
463 ExternalTabled,
465 Started { ref_index: ReferendumIndex, threshold: VoteThreshold },
467 Passed { ref_index: ReferendumIndex },
469 NotPassed { ref_index: ReferendumIndex },
471 Cancelled { ref_index: ReferendumIndex },
473 Delegated { who: T::AccountId, target: T::AccountId },
475 Undelegated { account: T::AccountId },
477 Vetoed { who: T::AccountId, proposal_hash: T::Hash, until: BlockNumberFor<T> },
479 Blacklisted { proposal_hash: T::Hash },
481 Voted { voter: T::AccountId, ref_index: ReferendumIndex, vote: AccountVote<BalanceOf<T>> },
483 Seconded { seconder: T::AccountId, prop_index: PropIndex },
485 ProposalCanceled { prop_index: PropIndex },
487 MetadataSet {
489 owner: MetadataOwner,
491 hash: T::Hash,
493 },
494 MetadataCleared {
496 owner: MetadataOwner,
498 hash: T::Hash,
500 },
501 MetadataTransferred {
503 prev_owner: MetadataOwner,
505 owner: MetadataOwner,
507 hash: T::Hash,
509 },
510 }
511
512 #[pallet::error]
513 pub enum Error<T> {
514 ValueLow,
516 ProposalMissing,
518 AlreadyCanceled,
520 DuplicateProposal,
522 ProposalBlacklisted,
524 NotSimpleMajority,
526 InvalidHash,
528 NoProposal,
530 AlreadyVetoed,
532 ReferendumInvalid,
534 NoneWaiting,
536 NotVoter,
538 NoPermission,
540 AlreadyDelegating,
542 InsufficientFunds,
544 NotDelegating,
546 VotesExist,
549 InstantNotAllowed,
551 Nonsense,
553 WrongUpperBound,
555 MaxVotesReached,
557 TooMany,
559 VotingPeriodLow,
561 PreimageNotExist,
563 }
564
565 #[pallet::hooks]
566 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
567 fn on_initialize(n: BlockNumberFor<T>) -> Weight {
569 Self::begin_block(n)
570 }
571 }
572
573 #[pallet::call]
574 impl<T: Config> Pallet<T> {
575 #[pallet::call_index(0)]
585 #[pallet::weight(T::WeightInfo::propose())]
586 pub fn propose(
587 origin: OriginFor<T>,
588 proposal: BoundedCallOf<T>,
589 #[pallet::compact] value: BalanceOf<T>,
590 ) -> DispatchResult {
591 let who = T::SubmitOrigin::ensure_origin(origin)?;
592 ensure!(value >= T::MinimumDeposit::get(), Error::<T>::ValueLow);
593
594 let index = PublicPropCount::<T>::get();
595 let real_prop_count = PublicProps::<T>::decode_len().unwrap_or(0) as u32;
596 let max_proposals = T::MaxProposals::get();
597 ensure!(real_prop_count < max_proposals, Error::<T>::TooMany);
598 let proposal_hash = proposal.hash();
599
600 if let Some((until, _)) = Blacklist::<T>::get(proposal_hash) {
601 ensure!(
602 frame_system::Pallet::<T>::block_number() >= until,
603 Error::<T>::ProposalBlacklisted,
604 );
605 }
606
607 T::Currency::reserve(&who, value)?;
608
609 let depositors = BoundedVec::<_, T::MaxDeposits>::truncate_from(vec![who.clone()]);
610 DepositOf::<T>::insert(index, (depositors, value));
611
612 PublicPropCount::<T>::put(index + 1);
613
614 PublicProps::<T>::try_append((index, proposal, who))
615 .map_err(|_| Error::<T>::TooMany)?;
616
617 Self::deposit_event(Event::<T>::Proposed { proposal_index: index, deposit: value });
618 Ok(())
619 }
620
621 #[pallet::call_index(1)]
628 #[pallet::weight(T::WeightInfo::second())]
629 pub fn second(
630 origin: OriginFor<T>,
631 #[pallet::compact] proposal: PropIndex,
632 ) -> DispatchResult {
633 let who = ensure_signed(origin)?;
634
635 let seconds = Self::len_of_deposit_of(proposal).ok_or(Error::<T>::ProposalMissing)?;
636 ensure!(seconds < T::MaxDeposits::get(), Error::<T>::TooMany);
637 let mut deposit = DepositOf::<T>::get(proposal).ok_or(Error::<T>::ProposalMissing)?;
638 T::Currency::reserve(&who, deposit.1)?;
639 let ok = deposit.0.try_push(who.clone()).is_ok();
640 debug_assert!(ok, "`seconds` is below static limit; `try_insert` should succeed; qed");
641 DepositOf::<T>::insert(proposal, deposit);
642 Self::deposit_event(Event::<T>::Seconded { seconder: who, prop_index: proposal });
643 Ok(())
644 }
645
646 #[pallet::call_index(2)]
654 #[pallet::weight(T::WeightInfo::vote_new().max(T::WeightInfo::vote_existing()))]
655 pub fn vote(
656 origin: OriginFor<T>,
657 #[pallet::compact] ref_index: ReferendumIndex,
658 vote: AccountVote<BalanceOf<T>>,
659 ) -> DispatchResult {
660 let who = ensure_signed(origin)?;
661 Self::try_vote(&who, ref_index, vote)
662 }
663
664 #[pallet::call_index(3)]
673 #[pallet::weight((T::WeightInfo::emergency_cancel(), DispatchClass::Operational))]
674 pub fn emergency_cancel(
675 origin: OriginFor<T>,
676 ref_index: ReferendumIndex,
677 ) -> DispatchResult {
678 T::CancellationOrigin::ensure_origin(origin)?;
679
680 let status = Self::referendum_status(ref_index)?;
681 let h = status.proposal.hash();
682 ensure!(!Cancellations::<T>::contains_key(h), Error::<T>::AlreadyCanceled);
683
684 Cancellations::<T>::insert(h, true);
685 Self::internal_cancel_referendum(ref_index);
686 Ok(())
687 }
688
689 #[pallet::call_index(4)]
696 #[pallet::weight(T::WeightInfo::external_propose())]
697 pub fn external_propose(
698 origin: OriginFor<T>,
699 proposal: BoundedCallOf<T>,
700 ) -> DispatchResult {
701 T::ExternalOrigin::ensure_origin(origin)?;
702 ensure!(!NextExternal::<T>::exists(), Error::<T>::DuplicateProposal);
703 if let Some((until, _)) = Blacklist::<T>::get(proposal.hash()) {
704 ensure!(
705 frame_system::Pallet::<T>::block_number() >= until,
706 Error::<T>::ProposalBlacklisted,
707 );
708 }
709 NextExternal::<T>::put((proposal, VoteThreshold::SuperMajorityApprove));
710 Ok(())
711 }
712
713 #[pallet::call_index(5)]
725 #[pallet::weight(T::WeightInfo::external_propose_majority())]
726 pub fn external_propose_majority(
727 origin: OriginFor<T>,
728 proposal: BoundedCallOf<T>,
729 ) -> DispatchResult {
730 T::ExternalMajorityOrigin::ensure_origin(origin)?;
731 NextExternal::<T>::put((proposal, VoteThreshold::SimpleMajority));
732 Ok(())
733 }
734
735 #[pallet::call_index(6)]
747 #[pallet::weight(T::WeightInfo::external_propose_default())]
748 pub fn external_propose_default(
749 origin: OriginFor<T>,
750 proposal: BoundedCallOf<T>,
751 ) -> DispatchResult {
752 T::ExternalDefaultOrigin::ensure_origin(origin)?;
753 NextExternal::<T>::put((proposal, VoteThreshold::SuperMajorityAgainst));
754 Ok(())
755 }
756
757 #[pallet::call_index(7)]
774 #[pallet::weight(T::WeightInfo::fast_track())]
775 pub fn fast_track(
776 origin: OriginFor<T>,
777 proposal_hash: T::Hash,
778 voting_period: BlockNumberFor<T>,
779 delay: BlockNumberFor<T>,
780 ) -> DispatchResult {
781 let maybe_ensure_instant = if voting_period < T::FastTrackVotingPeriod::get() {
786 Some(origin)
787 } else {
788 T::FastTrackOrigin::try_origin(origin).err()
789 };
790 if let Some(ensure_instant) = maybe_ensure_instant {
791 T::InstantOrigin::ensure_origin(ensure_instant)?;
792 ensure!(T::InstantAllowed::get(), Error::<T>::InstantNotAllowed);
793 }
794
795 ensure!(voting_period > Zero::zero(), Error::<T>::VotingPeriodLow);
796 let (ext_proposal, threshold) =
797 NextExternal::<T>::get().ok_or(Error::<T>::ProposalMissing)?;
798 ensure!(
799 threshold != VoteThreshold::SuperMajorityApprove,
800 Error::<T>::NotSimpleMajority,
801 );
802 ensure!(proposal_hash == ext_proposal.hash(), Error::<T>::InvalidHash);
803
804 NextExternal::<T>::kill();
805 let now = frame_system::Pallet::<T>::block_number();
806 let ref_index = Self::inject_referendum(
807 now.saturating_add(voting_period),
808 ext_proposal,
809 threshold,
810 delay,
811 );
812 Self::transfer_metadata(MetadataOwner::External, MetadataOwner::Referendum(ref_index));
813 Ok(())
814 }
815
816 #[pallet::call_index(8)]
826 #[pallet::weight(T::WeightInfo::veto_external())]
827 pub fn veto_external(origin: OriginFor<T>, proposal_hash: T::Hash) -> DispatchResult {
828 let who = T::VetoOrigin::ensure_origin(origin)?;
829
830 if let Some((ext_proposal, _)) = NextExternal::<T>::get() {
831 ensure!(proposal_hash == ext_proposal.hash(), Error::<T>::ProposalMissing);
832 } else {
833 return Err(Error::<T>::NoProposal.into())
834 }
835
836 let mut existing_vetoers =
837 Blacklist::<T>::get(&proposal_hash).map(|pair| pair.1).unwrap_or_default();
838 let insert_position =
839 existing_vetoers.binary_search(&who).err().ok_or(Error::<T>::AlreadyVetoed)?;
840 existing_vetoers
841 .try_insert(insert_position, who.clone())
842 .map_err(|_| Error::<T>::TooMany)?;
843
844 let until =
845 frame_system::Pallet::<T>::block_number().saturating_add(T::CooloffPeriod::get());
846 Blacklist::<T>::insert(&proposal_hash, (until, existing_vetoers));
847
848 Self::deposit_event(Event::<T>::Vetoed { who, proposal_hash, until });
849 NextExternal::<T>::kill();
850 Self::clear_metadata(MetadataOwner::External);
851 Ok(())
852 }
853
854 #[pallet::call_index(9)]
862 #[pallet::weight(T::WeightInfo::cancel_referendum())]
863 pub fn cancel_referendum(
864 origin: OriginFor<T>,
865 #[pallet::compact] ref_index: ReferendumIndex,
866 ) -> DispatchResult {
867 ensure_root(origin)?;
868 Self::internal_cancel_referendum(ref_index);
869 Ok(())
870 }
871
872 #[pallet::call_index(10)]
895 #[pallet::weight(T::WeightInfo::delegate(T::MaxVotes::get()))]
896 pub fn delegate(
897 origin: OriginFor<T>,
898 to: AccountIdLookupOf<T>,
899 conviction: Conviction,
900 balance: BalanceOf<T>,
901 ) -> DispatchResultWithPostInfo {
902 let who = ensure_signed(origin)?;
903 let to = T::Lookup::lookup(to)?;
904 let votes = Self::try_delegate(who, to, conviction, balance)?;
905
906 Ok(Some(T::WeightInfo::delegate(votes)).into())
907 }
908
909 #[pallet::call_index(11)]
924 #[pallet::weight(T::WeightInfo::undelegate(T::MaxVotes::get()))]
925 pub fn undelegate(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
926 let who = ensure_signed(origin)?;
927 let votes = Self::try_undelegate(who)?;
928 Ok(Some(T::WeightInfo::undelegate(votes)).into())
929 }
930
931 #[pallet::call_index(12)]
937 #[pallet::weight(T::WeightInfo::clear_public_proposals())]
938 pub fn clear_public_proposals(origin: OriginFor<T>) -> DispatchResult {
939 ensure_root(origin)?;
940 PublicProps::<T>::kill();
941 Ok(())
942 }
943
944 #[pallet::call_index(13)]
952 #[pallet::weight(T::WeightInfo::unlock_set(T::MaxVotes::get()).max(T::WeightInfo::unlock_remove(T::MaxVotes::get())))]
953 pub fn unlock(origin: OriginFor<T>, target: AccountIdLookupOf<T>) -> DispatchResult {
954 ensure_signed(origin)?;
955 let target = T::Lookup::lookup(target)?;
956 Self::update_lock(&target);
957 Ok(())
958 }
959
960 #[pallet::call_index(14)]
988 #[pallet::weight(T::WeightInfo::remove_vote(T::MaxVotes::get()))]
989 pub fn remove_vote(origin: OriginFor<T>, index: ReferendumIndex) -> DispatchResult {
990 let who = ensure_signed(origin)?;
991 Self::try_remove_vote(&who, index, UnvoteScope::Any)
992 }
993
994 #[pallet::call_index(15)]
1010 #[pallet::weight(T::WeightInfo::remove_other_vote(T::MaxVotes::get()))]
1011 pub fn remove_other_vote(
1012 origin: OriginFor<T>,
1013 target: AccountIdLookupOf<T>,
1014 index: ReferendumIndex,
1015 ) -> DispatchResult {
1016 let who = ensure_signed(origin)?;
1017 let target = T::Lookup::lookup(target)?;
1018 let scope = if target == who { UnvoteScope::Any } else { UnvoteScope::OnlyExpired };
1019 Self::try_remove_vote(&target, index, scope)?;
1020 Ok(())
1021 }
1022
1023 #[pallet::call_index(16)]
1039 #[pallet::weight((T::WeightInfo::blacklist(), DispatchClass::Operational))]
1040 pub fn blacklist(
1041 origin: OriginFor<T>,
1042 proposal_hash: T::Hash,
1043 maybe_ref_index: Option<ReferendumIndex>,
1044 ) -> DispatchResult {
1045 T::BlacklistOrigin::ensure_origin(origin)?;
1046
1047 let permanent =
1049 (BlockNumberFor::<T>::max_value(), BoundedVec::<T::AccountId, _>::default());
1050 Blacklist::<T>::insert(&proposal_hash, permanent);
1051
1052 PublicProps::<T>::mutate(|props| {
1054 if let Some(index) = props.iter().position(|p| p.1.hash() == proposal_hash) {
1055 let (prop_index, ..) = props.remove(index);
1056 if let Some((whos, amount)) = DepositOf::<T>::take(prop_index) {
1057 for who in whos.into_iter() {
1058 T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0);
1059 }
1060 }
1061 Self::clear_metadata(MetadataOwner::Proposal(prop_index));
1062 }
1063 });
1064
1065 if matches!(NextExternal::<T>::get(), Some((p, ..)) if p.hash() == proposal_hash) {
1067 NextExternal::<T>::kill();
1068 Self::clear_metadata(MetadataOwner::External);
1069 }
1070
1071 if let Some(ref_index) = maybe_ref_index {
1073 if let Ok(status) = Self::referendum_status(ref_index) {
1074 if status.proposal.hash() == proposal_hash {
1075 Self::internal_cancel_referendum(ref_index);
1076 }
1077 }
1078 }
1079
1080 Self::deposit_event(Event::<T>::Blacklisted { proposal_hash });
1081 Ok(())
1082 }
1083
1084 #[pallet::call_index(17)]
1092 #[pallet::weight(T::WeightInfo::cancel_proposal())]
1093 pub fn cancel_proposal(
1094 origin: OriginFor<T>,
1095 #[pallet::compact] prop_index: PropIndex,
1096 ) -> DispatchResult {
1097 T::CancelProposalOrigin::ensure_origin(origin)?;
1098
1099 PublicProps::<T>::mutate(|props| props.retain(|p| p.0 != prop_index));
1100 if let Some((whos, amount)) = DepositOf::<T>::take(prop_index) {
1101 for who in whos.into_iter() {
1102 T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0);
1103 }
1104 }
1105 Self::deposit_event(Event::<T>::ProposalCanceled { prop_index });
1106 Self::clear_metadata(MetadataOwner::Proposal(prop_index));
1107 Ok(())
1108 }
1109
1110 #[pallet::call_index(18)]
1126 #[pallet::weight(
1127 match (owner, maybe_hash) {
1128 (MetadataOwner::External, Some(_)) => T::WeightInfo::set_external_metadata(),
1129 (MetadataOwner::External, None) => T::WeightInfo::clear_external_metadata(),
1130 (MetadataOwner::Proposal(_), Some(_)) => T::WeightInfo::set_proposal_metadata(),
1131 (MetadataOwner::Proposal(_), None) => T::WeightInfo::clear_proposal_metadata(),
1132 (MetadataOwner::Referendum(_), Some(_)) => T::WeightInfo::set_referendum_metadata(),
1133 (MetadataOwner::Referendum(_), None) => T::WeightInfo::clear_referendum_metadata(),
1134 }
1135 )]
1136 pub fn set_metadata(
1137 origin: OriginFor<T>,
1138 owner: MetadataOwner,
1139 maybe_hash: Option<T::Hash>,
1140 ) -> DispatchResult {
1141 match owner {
1142 MetadataOwner::External => {
1143 let (_, threshold) = NextExternal::<T>::get().ok_or(Error::<T>::NoProposal)?;
1144 Self::ensure_external_origin(threshold, origin)?;
1145 },
1146 MetadataOwner::Proposal(index) => {
1147 let who = ensure_signed(origin)?;
1148 let (_, _, proposer) = Self::proposal(index)?;
1149 ensure!(proposer == who, Error::<T>::NoPermission);
1150 },
1151 MetadataOwner::Referendum(index) => {
1152 let is_root = ensure_signed_or_root(origin)?.is_none();
1153 ensure!(is_root || maybe_hash.is_none(), Error::<T>::NoPermission);
1154 ensure!(
1155 is_root || Self::referendum_status(index).is_err(),
1156 Error::<T>::NoPermission
1157 );
1158 },
1159 }
1160 if let Some(hash) = maybe_hash {
1161 ensure!(T::Preimages::len(&hash).is_some(), Error::<T>::PreimageNotExist);
1162 MetadataOf::<T>::insert(owner.clone(), hash);
1163 Self::deposit_event(Event::<T>::MetadataSet { owner, hash });
1164 } else {
1165 Self::clear_metadata(owner);
1166 }
1167 Ok(())
1168 }
1169 }
1170}
1171
1172pub trait EncodeInto: Encode {
1173 fn encode_into<T: AsMut<[u8]> + Default, H: sp_core::Hasher>(&self) -> T {
1174 let mut t = T::default();
1175 self.using_encoded(|data| {
1176 if data.len() <= t.as_mut().len() {
1177 t.as_mut()[0..data.len()].copy_from_slice(data);
1178 } else {
1179 let hash = H::hash(&data);
1182 let hash = hash.as_ref();
1183 let l = t.as_mut().len().min(hash.len());
1184 t.as_mut()[0..l].copy_from_slice(&hash[0..l]);
1185 }
1186 });
1187 t
1188 }
1189}
1190impl<T: Encode> EncodeInto for T {}
1191
1192impl<T: Config> Pallet<T> {
1193 pub fn backing_for(proposal: PropIndex) -> Option<BalanceOf<T>> {
1198 DepositOf::<T>::get(proposal).map(|(l, d)| d.saturating_mul((l.len() as u32).into()))
1199 }
1200
1201 pub fn maturing_referenda_at(
1203 n: BlockNumberFor<T>,
1204 ) -> Vec<(ReferendumIndex, ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>)>
1205 {
1206 let next = LowestUnbaked::<T>::get();
1207 let last = ReferendumCount::<T>::get();
1208 Self::maturing_referenda_at_inner(n, next..last)
1209 }
1210
1211 fn maturing_referenda_at_inner(
1212 n: BlockNumberFor<T>,
1213 range: core::ops::Range<PropIndex>,
1214 ) -> Vec<(ReferendumIndex, ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>)>
1215 {
1216 range
1217 .into_iter()
1218 .map(|i| (i, ReferendumInfoOf::<T>::get(i)))
1219 .filter_map(|(i, maybe_info)| match maybe_info {
1220 Some(ReferendumInfo::Ongoing(status)) => Some((i, status)),
1221 _ => None,
1222 })
1223 .filter(|(_, status)| status.end == n)
1224 .collect()
1225 }
1226
1227 pub fn internal_start_referendum(
1231 proposal: BoundedCallOf<T>,
1232 threshold: VoteThreshold,
1233 delay: BlockNumberFor<T>,
1234 ) -> ReferendumIndex {
1235 Pallet::<T>::inject_referendum(
1236 frame_system::Pallet::<T>::block_number().saturating_add(T::VotingPeriod::get()),
1237 proposal,
1238 threshold,
1239 delay,
1240 )
1241 }
1242
1243 pub fn internal_cancel_referendum(ref_index: ReferendumIndex) {
1245 Self::deposit_event(Event::<T>::Cancelled { ref_index });
1246 ReferendumInfoOf::<T>::remove(ref_index);
1247 Self::clear_metadata(MetadataOwner::Referendum(ref_index));
1248 }
1249
1250 fn ensure_ongoing(
1254 r: ReferendumInfo<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>,
1255 ) -> Result<ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>, DispatchError>
1256 {
1257 match r {
1258 ReferendumInfo::Ongoing(s) => Ok(s),
1259 _ => Err(Error::<T>::ReferendumInvalid.into()),
1260 }
1261 }
1262
1263 fn referendum_status(
1264 ref_index: ReferendumIndex,
1265 ) -> Result<ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>, DispatchError>
1266 {
1267 let info = ReferendumInfoOf::<T>::get(ref_index).ok_or(Error::<T>::ReferendumInvalid)?;
1268 Self::ensure_ongoing(info)
1269 }
1270
1271 fn try_vote(
1273 who: &T::AccountId,
1274 ref_index: ReferendumIndex,
1275 vote: AccountVote<BalanceOf<T>>,
1276 ) -> DispatchResult {
1277 let mut status = Self::referendum_status(ref_index)?;
1278 ensure!(vote.balance() <= T::Currency::free_balance(who), Error::<T>::InsufficientFunds);
1279 VotingOf::<T>::try_mutate(who, |voting| -> DispatchResult {
1280 if let Voting::Direct { ref mut votes, delegations, .. } = voting {
1281 match votes.binary_search_by_key(&ref_index, |i| i.0) {
1282 Ok(i) => {
1283 status.tally.remove(votes[i].1).ok_or(ArithmeticError::Underflow)?;
1285 if let Some(approve) = votes[i].1.as_standard() {
1286 status.tally.reduce(approve, *delegations);
1287 }
1288 votes[i].1 = vote;
1289 },
1290 Err(i) => {
1291 votes
1292 .try_insert(i, (ref_index, vote))
1293 .map_err(|_| Error::<T>::MaxVotesReached)?;
1294 },
1295 }
1296 Self::deposit_event(Event::<T>::Voted { voter: who.clone(), ref_index, vote });
1297 status.tally.add(vote).ok_or(ArithmeticError::Overflow)?;
1299 if let Some(approve) = vote.as_standard() {
1300 status.tally.increase(approve, *delegations);
1301 }
1302 Ok(())
1303 } else {
1304 Err(Error::<T>::AlreadyDelegating.into())
1305 }
1306 })?;
1307 T::Currency::extend_lock(
1310 DEMOCRACY_ID,
1311 who,
1312 vote.balance(),
1313 WithdrawReasons::except(WithdrawReasons::RESERVE),
1314 );
1315 ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
1316 Ok(())
1317 }
1318
1319 fn try_remove_vote(
1326 who: &T::AccountId,
1327 ref_index: ReferendumIndex,
1328 scope: UnvoteScope,
1329 ) -> DispatchResult {
1330 let info = ReferendumInfoOf::<T>::get(ref_index);
1331 VotingOf::<T>::try_mutate(who, |voting| -> DispatchResult {
1332 if let Voting::Direct { ref mut votes, delegations, ref mut prior } = voting {
1333 let i = votes
1334 .binary_search_by_key(&ref_index, |i| i.0)
1335 .map_err(|_| Error::<T>::NotVoter)?;
1336 match info {
1337 Some(ReferendumInfo::Ongoing(mut status)) => {
1338 ensure!(matches!(scope, UnvoteScope::Any), Error::<T>::NoPermission);
1339 status.tally.remove(votes[i].1).ok_or(ArithmeticError::Underflow)?;
1341 if let Some(approve) = votes[i].1.as_standard() {
1342 status.tally.reduce(approve, *delegations);
1343 }
1344 ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
1345 },
1346 Some(ReferendumInfo::Finished { end, approved }) => {
1347 if let Some((lock_periods, balance)) = votes[i].1.locked_if(approved) {
1348 let unlock_at = end.saturating_add(
1349 T::VoteLockingPeriod::get().saturating_mul(lock_periods.into()),
1350 );
1351 let now = frame_system::Pallet::<T>::block_number();
1352 if now < unlock_at {
1353 ensure!(
1354 matches!(scope, UnvoteScope::Any),
1355 Error::<T>::NoPermission
1356 );
1357 prior.accumulate(unlock_at, balance)
1358 }
1359 }
1360 },
1361 None => {}, }
1363 votes.remove(i);
1364 }
1365 Ok(())
1366 })?;
1367 Ok(())
1368 }
1369
1370 fn increase_upstream_delegation(who: &T::AccountId, amount: Delegations<BalanceOf<T>>) -> u32 {
1372 VotingOf::<T>::mutate(who, |voting| match voting {
1373 Voting::Delegating { delegations, .. } => {
1374 *delegations = delegations.saturating_add(amount);
1376 1
1377 },
1378 Voting::Direct { votes, delegations, .. } => {
1379 *delegations = delegations.saturating_add(amount);
1380 for &(ref_index, account_vote) in votes.iter() {
1381 if let AccountVote::Standard { vote, .. } = account_vote {
1382 ReferendumInfoOf::<T>::mutate(ref_index, |maybe_info| {
1383 if let Some(ReferendumInfo::Ongoing(ref mut status)) = maybe_info {
1384 status.tally.increase(vote.aye, amount);
1385 }
1386 });
1387 }
1388 }
1389 votes.len() as u32
1390 },
1391 })
1392 }
1393
1394 fn reduce_upstream_delegation(who: &T::AccountId, amount: Delegations<BalanceOf<T>>) -> u32 {
1396 VotingOf::<T>::mutate(who, |voting| match voting {
1397 Voting::Delegating { delegations, .. } => {
1398 *delegations = delegations.saturating_sub(amount);
1400 1
1401 },
1402 Voting::Direct { votes, delegations, .. } => {
1403 *delegations = delegations.saturating_sub(amount);
1404 for &(ref_index, account_vote) in votes.iter() {
1405 if let AccountVote::Standard { vote, .. } = account_vote {
1406 ReferendumInfoOf::<T>::mutate(ref_index, |maybe_info| {
1407 if let Some(ReferendumInfo::Ongoing(ref mut status)) = maybe_info {
1408 status.tally.reduce(vote.aye, amount);
1409 }
1410 });
1411 }
1412 }
1413 votes.len() as u32
1414 },
1415 })
1416 }
1417
1418 fn try_delegate(
1422 who: T::AccountId,
1423 target: T::AccountId,
1424 conviction: Conviction,
1425 balance: BalanceOf<T>,
1426 ) -> Result<u32, DispatchError> {
1427 ensure!(who != target, Error::<T>::Nonsense);
1428 ensure!(balance <= T::Currency::free_balance(&who), Error::<T>::InsufficientFunds);
1429 let votes = VotingOf::<T>::try_mutate(&who, |voting| -> Result<u32, DispatchError> {
1430 let mut old = Voting::Delegating {
1431 balance,
1432 target: target.clone(),
1433 conviction,
1434 delegations: Default::default(),
1435 prior: Default::default(),
1436 };
1437 core::mem::swap(&mut old, voting);
1438 match old {
1439 Voting::Delegating {
1440 balance, target, conviction, delegations, mut prior, ..
1441 } => {
1442 Self::reduce_upstream_delegation(&target, conviction.votes(balance));
1444 let now = frame_system::Pallet::<T>::block_number();
1445 let lock_periods = conviction.lock_periods().into();
1446 let unlock_block = now
1447 .saturating_add(T::VoteLockingPeriod::get().saturating_mul(lock_periods));
1448 prior.accumulate(unlock_block, balance);
1449 voting.set_common(delegations, prior);
1450 },
1451 Voting::Direct { votes, delegations, prior } => {
1452 ensure!(votes.is_empty(), Error::<T>::VotesExist);
1454 voting.set_common(delegations, prior);
1455 },
1456 }
1457 let votes = Self::increase_upstream_delegation(&target, conviction.votes(balance));
1458 T::Currency::extend_lock(
1461 DEMOCRACY_ID,
1462 &who,
1463 balance,
1464 WithdrawReasons::except(WithdrawReasons::RESERVE),
1465 );
1466 Ok(votes)
1467 })?;
1468 Self::deposit_event(Event::<T>::Delegated { who, target });
1469 Ok(votes)
1470 }
1471
1472 fn try_undelegate(who: T::AccountId) -> Result<u32, DispatchError> {
1476 let votes = VotingOf::<T>::try_mutate(&who, |voting| -> Result<u32, DispatchError> {
1477 let mut old = Voting::default();
1478 core::mem::swap(&mut old, voting);
1479 match old {
1480 Voting::Delegating { balance, target, conviction, delegations, mut prior } => {
1481 let votes =
1483 Self::reduce_upstream_delegation(&target, conviction.votes(balance));
1484 let now = frame_system::Pallet::<T>::block_number();
1485 let lock_periods = conviction.lock_periods().into();
1486 let unlock_block = now
1487 .saturating_add(T::VoteLockingPeriod::get().saturating_mul(lock_periods));
1488 prior.accumulate(unlock_block, balance);
1489 voting.set_common(delegations, prior);
1490
1491 Ok(votes)
1492 },
1493 Voting::Direct { .. } => Err(Error::<T>::NotDelegating.into()),
1494 }
1495 })?;
1496 Self::deposit_event(Event::<T>::Undelegated { account: who });
1497 Ok(votes)
1498 }
1499
1500 fn update_lock(who: &T::AccountId) {
1503 let lock_needed = VotingOf::<T>::mutate(who, |voting| {
1504 voting.rejig(frame_system::Pallet::<T>::block_number());
1505 voting.locked_balance()
1506 });
1507 if lock_needed.is_zero() {
1508 T::Currency::remove_lock(DEMOCRACY_ID, who);
1509 } else {
1510 T::Currency::set_lock(
1511 DEMOCRACY_ID,
1512 who,
1513 lock_needed,
1514 WithdrawReasons::except(WithdrawReasons::RESERVE),
1515 );
1516 }
1517 }
1518
1519 fn inject_referendum(
1521 end: BlockNumberFor<T>,
1522 proposal: BoundedCallOf<T>,
1523 threshold: VoteThreshold,
1524 delay: BlockNumberFor<T>,
1525 ) -> ReferendumIndex {
1526 let ref_index = ReferendumCount::<T>::get();
1527 ReferendumCount::<T>::put(ref_index + 1);
1528 let status =
1529 ReferendumStatus { end, proposal, threshold, delay, tally: Default::default() };
1530 let item = ReferendumInfo::Ongoing(status);
1531 ReferendumInfoOf::<T>::insert(ref_index, item);
1532 Self::deposit_event(Event::<T>::Started { ref_index, threshold });
1533 ref_index
1534 }
1535
1536 fn launch_next(now: BlockNumberFor<T>) -> DispatchResult {
1538 if LastTabledWasExternal::<T>::take() {
1539 Self::launch_public(now).or_else(|_| Self::launch_external(now))
1540 } else {
1541 Self::launch_external(now).or_else(|_| Self::launch_public(now))
1542 }
1543 .map_err(|_| Error::<T>::NoneWaiting.into())
1544 }
1545
1546 fn launch_external(now: BlockNumberFor<T>) -> DispatchResult {
1548 if let Some((proposal, threshold)) = NextExternal::<T>::take() {
1549 LastTabledWasExternal::<T>::put(true);
1550 Self::deposit_event(Event::<T>::ExternalTabled);
1551 let ref_index = Self::inject_referendum(
1552 now.saturating_add(T::VotingPeriod::get()),
1553 proposal,
1554 threshold,
1555 T::EnactmentPeriod::get(),
1556 );
1557 Self::transfer_metadata(MetadataOwner::External, MetadataOwner::Referendum(ref_index));
1558 Ok(())
1559 } else {
1560 return Err(Error::<T>::NoneWaiting.into())
1561 }
1562 }
1563
1564 fn launch_public(now: BlockNumberFor<T>) -> DispatchResult {
1566 let mut public_props = PublicProps::<T>::get();
1567 if let Some((winner_index, _)) = public_props.iter().enumerate().max_by_key(
1568 |x| Self::backing_for((x.1).0).defensive_unwrap_or_else(Zero::zero),
1570 ) {
1571 let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
1572 PublicProps::<T>::put(public_props);
1573
1574 if let Some((depositors, deposit)) = DepositOf::<T>::take(prop_index) {
1575 for d in depositors.iter() {
1577 T::Currency::unreserve(d, deposit);
1578 }
1579 Self::deposit_event(Event::<T>::Tabled { proposal_index: prop_index, deposit });
1580 let ref_index = Self::inject_referendum(
1581 now.saturating_add(T::VotingPeriod::get()),
1582 proposal,
1583 VoteThreshold::SuperMajorityApprove,
1584 T::EnactmentPeriod::get(),
1585 );
1586 Self::transfer_metadata(
1587 MetadataOwner::Proposal(prop_index),
1588 MetadataOwner::Referendum(ref_index),
1589 )
1590 }
1591 Ok(())
1592 } else {
1593 return Err(Error::<T>::NoneWaiting.into())
1594 }
1595 }
1596
1597 fn bake_referendum(
1598 now: BlockNumberFor<T>,
1599 index: ReferendumIndex,
1600 status: ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>,
1601 ) -> bool {
1602 let total_issuance = T::Currency::total_issuance();
1603 let approved = status.threshold.approved(status.tally, total_issuance);
1604
1605 if approved {
1606 Self::deposit_event(Event::<T>::Passed { ref_index: index });
1607
1608 let when = now.saturating_add(status.delay.max(One::one()));
1610 if T::Scheduler::schedule_named(
1611 (DEMOCRACY_ID, index).encode_into::<_, T::Hashing>(),
1612 DispatchTime::At(when),
1613 None,
1614 63,
1615 frame_system::RawOrigin::Root.into(),
1616 status.proposal,
1617 )
1618 .is_err()
1619 {
1620 frame_support::print("LOGIC ERROR: bake_referendum/schedule_named failed");
1621 }
1622 } else {
1623 Self::deposit_event(Event::<T>::NotPassed { ref_index: index });
1624 }
1625
1626 approved
1627 }
1628
1629 fn begin_block(now: BlockNumberFor<T>) -> Weight {
1636 let max_block_weight = T::BlockWeights::get().max_block;
1637 let mut weight = Weight::zero();
1638
1639 let next = LowestUnbaked::<T>::get();
1640 let last = ReferendumCount::<T>::get();
1641 let r = last.saturating_sub(next);
1642
1643 if (now % T::LaunchPeriod::get()).is_zero() {
1645 if Self::launch_next(now).is_ok() {
1648 weight = max_block_weight;
1649 } else {
1650 weight.saturating_accrue(T::WeightInfo::on_initialize_base_with_launch_period(r));
1651 }
1652 } else {
1653 weight.saturating_accrue(T::WeightInfo::on_initialize_base(r));
1654 }
1655
1656 for (index, info) in Self::maturing_referenda_at_inner(now, next..last).into_iter() {
1658 let approved = Self::bake_referendum(now, index, info);
1659 ReferendumInfoOf::<T>::insert(index, ReferendumInfo::Finished { end: now, approved });
1660 weight = max_block_weight;
1661 }
1662
1663 LowestUnbaked::<T>::mutate(|ref_index| {
1672 while *ref_index < last &&
1673 ReferendumInfoOf::<T>::get(*ref_index)
1674 .map_or(true, |info| matches!(info, ReferendumInfo::Finished { .. }))
1675 {
1676 *ref_index += 1
1677 }
1678 });
1679
1680 weight
1681 }
1682
1683 fn len_of_deposit_of(proposal: PropIndex) -> Option<u32> {
1687 decode_compact_u32_at(&DepositOf::<T>::hashed_key_for(proposal))
1690 }
1691
1692 fn proposal(index: PropIndex) -> Result<(PropIndex, BoundedCallOf<T>, T::AccountId), Error<T>> {
1694 PublicProps::<T>::get()
1695 .into_iter()
1696 .find(|(prop_index, _, _)| prop_index == &index)
1697 .ok_or(Error::<T>::ProposalMissing)
1698 }
1699
1700 fn clear_metadata(owner: MetadataOwner) {
1702 if let Some(hash) = MetadataOf::<T>::take(&owner) {
1703 Self::deposit_event(Event::<T>::MetadataCleared { owner, hash });
1704 }
1705 }
1706
1707 fn transfer_metadata(owner: MetadataOwner, new_owner: MetadataOwner) {
1709 if let Some(hash) = MetadataOf::<T>::take(&owner) {
1710 MetadataOf::<T>::insert(&new_owner, hash);
1711 Self::deposit_event(Event::<T>::MetadataTransferred {
1712 prev_owner: owner,
1713 owner: new_owner,
1714 hash,
1715 });
1716 }
1717 }
1718
1719 fn ensure_external_origin(
1721 threshold: VoteThreshold,
1722 origin: OriginFor<T>,
1723 ) -> Result<(), BadOrigin> {
1724 match threshold {
1725 VoteThreshold::SuperMajorityApprove => {
1726 T::ExternalOrigin::ensure_origin(origin)?;
1727 },
1728 VoteThreshold::SuperMajorityAgainst => {
1729 T::ExternalDefaultOrigin::ensure_origin(origin)?;
1730 },
1731 VoteThreshold::SimpleMajority => {
1732 T::ExternalMajorityOrigin::ensure_origin(origin)?;
1733 },
1734 };
1735 Ok(())
1736 }
1737}
1738
1739fn decode_compact_u32_at(key: &[u8]) -> Option<u32> {
1741 let mut buf = [0u8; 5];
1743 let bytes = sp_io::storage::read(key, &mut buf, 0)?;
1744 let mut input = &buf[0..buf.len().min(bytes as usize)];
1746 match codec::Compact::<u32>::decode(&mut input) {
1747 Ok(c) => Some(c.0),
1748 Err(_) => {
1749 sp_runtime::print("Failed to decode compact u32 at:");
1750 sp_runtime::print(key);
1751 None
1752 },
1753 }
1754}