1#![recursion_limit = "256"]
65#![cfg_attr(not(feature = "std"), no_std)]
66
67extern crate alloc;
68
69use alloc::boxed::Box;
70use codec::{Codec, Encode};
71use core::fmt::Debug;
72use frame_support::{
73 dispatch::DispatchResult,
74 ensure,
75 traits::{
76 schedule::{
77 v3::{Anon as ScheduleAnon, Named as ScheduleNamed},
78 DispatchTime,
79 },
80 Currency, LockIdentifier, OnUnbalanced, OriginTrait, PollStatus, Polling, QueryPreimage,
81 ReservableCurrency, StorePreimage, VoteTally,
82 },
83 BoundedVec,
84};
85use scale_info::TypeInfo;
86use sp_runtime::{
87 traits::{AtLeast32BitUnsigned, Bounded, Dispatchable, One, Saturating, Zero},
88 DispatchError, Perbill,
89};
90
91mod branch;
92pub mod migration;
93mod types;
94pub mod weights;
95
96use self::branch::{BeginDecidingBranch, OneFewerDecidingBranch, ServiceBranch};
97pub use self::{
98 pallet::*,
99 types::{
100 BalanceOf, BlockNumberFor, BoundedCallOf, CallOf, ConstTrackInfo, Curve, DecidingStatus,
101 DecidingStatusOf, Deposit, InsertSorted, NegativeImbalanceOf, PalletsOriginOf,
102 ReferendumIndex, ReferendumInfo, ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf,
103 ScheduleAddressOf, StringLike, TallyOf, Track, TrackIdOf, TrackInfo, TrackInfoOf,
104 TracksInfo, VotesOf,
105 },
106 weights::WeightInfo,
107};
108pub use alloc::vec::Vec;
109use sp_runtime::traits::BlockNumberProvider;
110
111#[cfg(test)]
112mod mock;
113#[cfg(test)]
114mod tests;
115
116#[cfg(feature = "runtime-benchmarks")]
117pub mod benchmarking;
118
119pub use frame_support::traits::Get;
120
121const ASSEMBLY_ID: LockIdentifier = *b"assembly";
122
123#[frame_support::pallet]
124pub mod pallet {
125 use super::*;
126 use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg};
127 use frame_system::pallet_prelude::{
128 ensure_root, ensure_signed, ensure_signed_or_root, BlockNumberFor as SystemBlockNumberFor,
129 OriginFor,
130 };
131
132 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
134
135 #[pallet::pallet]
136 #[pallet::storage_version(STORAGE_VERSION)]
137 pub struct Pallet<T, I = ()>(_);
138
139 #[pallet::config]
140 pub trait Config<I: 'static = ()>: frame_system::Config + Sized {
141 type RuntimeCall: Parameter
143 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
144 + From<Call<Self, I>>
145 + IsType<<Self as frame_system::Config>::RuntimeCall>
146 + From<frame_system::Call<Self>>;
147 #[allow(deprecated)]
148 type RuntimeEvent: From<Event<Self, I>>
149 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
150 type WeightInfo: WeightInfo;
152 type Scheduler: ScheduleAnon<
154 BlockNumberFor<Self, I>,
155 CallOf<Self, I>,
156 PalletsOriginOf<Self>,
157 Hasher = Self::Hashing,
158 > + ScheduleNamed<
159 BlockNumberFor<Self, I>,
160 CallOf<Self, I>,
161 PalletsOriginOf<Self>,
162 Hasher = Self::Hashing,
163 >;
164 type Currency: ReservableCurrency<Self::AccountId>;
166 type SubmitOrigin: EnsureOriginWithArg<
169 Self::RuntimeOrigin,
170 PalletsOriginOf<Self>,
171 Success = Self::AccountId,
172 >;
173 type CancelOrigin: EnsureOrigin<Self::RuntimeOrigin>;
175 type KillOrigin: EnsureOrigin<Self::RuntimeOrigin>;
177 type Slash: OnUnbalanced<NegativeImbalanceOf<Self, I>>;
179 type Votes: AtLeast32BitUnsigned + Copy + Parameter + Member + MaxEncodedLen;
181 type Tally: VoteTally<Self::Votes, TrackIdOf<Self, I>>
183 + Clone
184 + Codec
185 + Eq
186 + Debug
187 + TypeInfo
188 + MaxEncodedLen;
189
190 #[pallet::constant]
193 type SubmissionDeposit: Get<BalanceOf<Self, I>>;
194
195 #[pallet::constant]
197 type MaxQueued: Get<u32>;
198
199 #[pallet::constant]
202 type UndecidingTimeout: Get<BlockNumberFor<Self, I>>;
203
204 #[pallet::constant]
208 type AlarmInterval: Get<BlockNumberFor<Self, I>>;
209
210 type Tracks: TracksInfo<
213 BalanceOf<Self, I>,
214 BlockNumberFor<Self, I>,
215 RuntimeOrigin = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin,
216 >;
217
218 type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
220
221 type BlockNumberProvider: BlockNumberProvider;
225 }
226
227 #[pallet::extra_constants]
228 impl<T: Config<I>, I: 'static> Pallet<T, I> {
229 #[pallet::constant_name(Tracks)]
233 fn tracks() -> Vec<(TrackIdOf<T, I>, ConstTrackInfo<BalanceOf<T, I>, BlockNumberFor<T, I>>)>
234 {
235 T::Tracks::tracks()
236 .map(|t| t.into_owned())
237 .map(|Track { id, info }| {
238 (
239 id,
240 ConstTrackInfo {
241 name: StringLike(info.name),
242 max_deciding: info.max_deciding,
243 decision_deposit: info.decision_deposit,
244 prepare_period: info.prepare_period,
245 decision_period: info.decision_period,
246 confirm_period: info.confirm_period,
247 min_enactment_period: info.min_enactment_period,
248 min_approval: info.min_approval,
249 min_support: info.min_support,
250 },
251 )
252 })
253 .collect()
254 }
255 }
256
257 #[pallet::storage]
259 pub type ReferendumCount<T, I = ()> = StorageValue<_, ReferendumIndex, ValueQuery>;
260
261 #[pallet::storage]
263 pub type ReferendumInfoFor<T: Config<I>, I: 'static = ()> =
264 StorageMap<_, Blake2_128Concat, ReferendumIndex, ReferendumInfoOf<T, I>>;
265
266 #[pallet::storage]
271 pub type TrackQueue<T: Config<I>, I: 'static = ()> = StorageMap<
272 _,
273 Twox64Concat,
274 TrackIdOf<T, I>,
275 BoundedVec<(ReferendumIndex, T::Votes), T::MaxQueued>,
276 ValueQuery,
277 >;
278
279 #[pallet::storage]
281 pub type DecidingCount<T: Config<I>, I: 'static = ()> =
282 StorageMap<_, Twox64Concat, TrackIdOf<T, I>, u32, ValueQuery>;
283
284 #[pallet::storage]
291 pub type MetadataOf<T: Config<I>, I: 'static = ()> =
292 StorageMap<_, Blake2_128Concat, ReferendumIndex, T::Hash>;
293
294 #[pallet::event]
295 #[pallet::generate_deposit(pub(super) fn deposit_event)]
296 pub enum Event<T: Config<I>, I: 'static = ()> {
297 Submitted {
299 index: ReferendumIndex,
301 track: TrackIdOf<T, I>,
303 proposal: BoundedCallOf<T, I>,
305 },
306 DecisionDepositPlaced {
308 index: ReferendumIndex,
310 who: T::AccountId,
312 amount: BalanceOf<T, I>,
314 },
315 DecisionDepositRefunded {
317 index: ReferendumIndex,
319 who: T::AccountId,
321 amount: BalanceOf<T, I>,
323 },
324 DepositSlashed {
326 who: T::AccountId,
328 amount: BalanceOf<T, I>,
330 },
331 DecisionStarted {
333 index: ReferendumIndex,
335 track: TrackIdOf<T, I>,
337 proposal: BoundedCallOf<T, I>,
339 tally: T::Tally,
341 },
342 ConfirmStarted {
343 index: ReferendumIndex,
345 },
346 ConfirmAborted {
347 index: ReferendumIndex,
349 },
350 Confirmed {
352 index: ReferendumIndex,
354 tally: T::Tally,
356 },
357 Approved {
359 index: ReferendumIndex,
361 },
362 Rejected {
364 index: ReferendumIndex,
366 tally: T::Tally,
368 },
369 TimedOut {
371 index: ReferendumIndex,
373 tally: T::Tally,
375 },
376 Cancelled {
378 index: ReferendumIndex,
380 tally: T::Tally,
382 },
383 Killed {
385 index: ReferendumIndex,
387 tally: T::Tally,
389 },
390 SubmissionDepositRefunded {
392 index: ReferendumIndex,
394 who: T::AccountId,
396 amount: BalanceOf<T, I>,
398 },
399 MetadataSet {
401 index: ReferendumIndex,
403 hash: T::Hash,
405 },
406 MetadataCleared {
408 index: ReferendumIndex,
410 hash: T::Hash,
412 },
413 }
414
415 #[pallet::error]
416 pub enum Error<T, I = ()> {
417 NotOngoing,
419 HasDeposit,
421 BadTrack,
423 Full,
425 QueueEmpty,
427 BadReferendum,
429 NothingToDo,
431 NoTrack,
433 Unfinished,
435 NoPermission,
437 NoDeposit,
439 BadStatus,
441 PreimageNotExist,
443 PreimageStoredWithDifferentLength,
445 }
446
447 #[pallet::hooks]
448 impl<T: Config<I>, I: 'static> Hooks<SystemBlockNumberFor<T>> for Pallet<T, I> {
449 #[cfg(feature = "try-runtime")]
450 fn try_state(_n: SystemBlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
451 Self::do_try_state()?;
452 Ok(())
453 }
454
455 #[cfg(any(feature = "std", test))]
456 fn integrity_test() {
457 T::Tracks::check_integrity().expect("Static tracks configuration is valid.");
458 }
459 }
460
461 #[pallet::call]
462 impl<T: Config<I>, I: 'static> Pallet<T, I> {
463 #[pallet::call_index(0)]
473 #[pallet::weight(T::WeightInfo::submit())]
474 pub fn submit(
475 origin: OriginFor<T>,
476 proposal_origin: Box<PalletsOriginOf<T>>,
477 proposal: BoundedCallOf<T, I>,
478 enactment_moment: DispatchTime<BlockNumberFor<T, I>>,
479 ) -> DispatchResult {
480 let proposal_origin = *proposal_origin;
481 let who = T::SubmitOrigin::ensure_origin(origin, &proposal_origin)?;
482
483 if let (Some(preimage_len), Some(proposal_len)) =
486 (proposal.lookup_hash().and_then(|h| T::Preimages::len(&h)), proposal.lookup_len())
487 {
488 if preimage_len != proposal_len {
489 return Err(Error::<T, I>::PreimageStoredWithDifferentLength.into());
490 }
491 }
492
493 let track =
494 T::Tracks::track_for(&proposal_origin).map_err(|_| Error::<T, I>::NoTrack)?;
495 let submission_deposit = Self::take_deposit(who, T::SubmissionDeposit::get())?;
496 let index = ReferendumCount::<T, I>::mutate(|x| {
497 let r = *x;
498 *x += 1;
499 r
500 });
501 let now = T::BlockNumberProvider::current_block_number();
502 let nudge_call =
503 T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index }))?;
504 let status = ReferendumStatus {
505 track,
506 origin: proposal_origin,
507 proposal: proposal.clone(),
508 enactment: enactment_moment,
509 submitted: now,
510 submission_deposit,
511 decision_deposit: None,
512 deciding: None,
513 tally: TallyOf::<T, I>::new(track),
514 in_queue: false,
515 alarm: Self::set_alarm(nudge_call, now.saturating_add(T::UndecidingTimeout::get())),
516 };
517 ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status));
518
519 Self::deposit_event(Event::<T, I>::Submitted { index, track, proposal });
520 Ok(())
521 }
522
523 #[pallet::call_index(1)]
532 #[pallet::weight(ServiceBranch::max_weight_of_deposit::<T, I>())]
533 pub fn place_decision_deposit(
534 origin: OriginFor<T>,
535 index: ReferendumIndex,
536 ) -> DispatchResultWithPostInfo {
537 let who = ensure_signed(origin)?;
538 let mut status = Self::ensure_ongoing(index)?;
539 ensure!(status.decision_deposit.is_none(), Error::<T, I>::HasDeposit);
540 let track = T::Tracks::info(status.track).ok_or(Error::<T, I>::NoTrack)?;
541 status.decision_deposit =
542 Some(Self::take_deposit(who.clone(), track.decision_deposit)?);
543 let now = T::BlockNumberProvider::current_block_number();
544 let (info, _, branch) = Self::service_referendum(now, index, status);
545 ReferendumInfoFor::<T, I>::insert(index, info);
546 let e =
547 Event::<T, I>::DecisionDepositPlaced { index, who, amount: track.decision_deposit };
548 Self::deposit_event(e);
549 Ok(branch.weight_of_deposit::<T, I>().into())
550 }
551
552 #[pallet::call_index(2)]
560 #[pallet::weight(T::WeightInfo::refund_decision_deposit())]
561 pub fn refund_decision_deposit(
562 origin: OriginFor<T>,
563 index: ReferendumIndex,
564 ) -> DispatchResult {
565 ensure_signed_or_root(origin)?;
566 let mut info =
567 ReferendumInfoFor::<T, I>::get(index).ok_or(Error::<T, I>::BadReferendum)?;
568 let deposit = info
569 .take_decision_deposit()
570 .map_err(|_| Error::<T, I>::Unfinished)?
571 .ok_or(Error::<T, I>::NoDeposit)?;
572 Self::refund_deposit(Some(deposit.clone()));
573 ReferendumInfoFor::<T, I>::insert(index, info);
574 let e = Event::<T, I>::DecisionDepositRefunded {
575 index,
576 who: deposit.who,
577 amount: deposit.amount,
578 };
579 Self::deposit_event(e);
580 Ok(())
581 }
582
583 #[pallet::call_index(3)]
590 #[pallet::weight(T::WeightInfo::cancel())]
591 pub fn cancel(origin: OriginFor<T>, index: ReferendumIndex) -> DispatchResult {
592 T::CancelOrigin::ensure_origin(origin)?;
593 let status = Self::ensure_ongoing(index)?;
594 if let Some((_, last_alarm)) = status.alarm {
595 let _ = T::Scheduler::cancel(last_alarm);
596 }
597 if status.deciding.is_some() {
598 Self::note_one_fewer_deciding(status.track);
599 }
600 Self::deposit_event(Event::<T, I>::Cancelled { index, tally: status.tally });
601 let info = ReferendumInfo::Cancelled(
602 T::BlockNumberProvider::current_block_number(),
603 Some(status.submission_deposit),
604 status.decision_deposit,
605 );
606 ReferendumInfoFor::<T, I>::insert(index, info);
607 Ok(())
608 }
609
610 #[pallet::call_index(4)]
617 #[pallet::weight(T::WeightInfo::kill())]
618 pub fn kill(origin: OriginFor<T>, index: ReferendumIndex) -> DispatchResult {
619 T::KillOrigin::ensure_origin(origin)?;
620 let status = Self::ensure_ongoing(index)?;
621 if let Some((_, last_alarm)) = status.alarm {
622 let _ = T::Scheduler::cancel(last_alarm);
623 }
624 if status.deciding.is_some() {
625 Self::note_one_fewer_deciding(status.track);
626 }
627 Self::deposit_event(Event::<T, I>::Killed { index, tally: status.tally });
628 Self::slash_deposit(Some(status.submission_deposit.clone()));
629 Self::slash_deposit(status.decision_deposit.clone());
630 Self::do_clear_metadata(index);
631 let info = ReferendumInfo::Killed(T::BlockNumberProvider::current_block_number());
632 ReferendumInfoFor::<T, I>::insert(index, info);
633 Ok(())
634 }
635
636 #[pallet::call_index(5)]
641 #[pallet::weight(ServiceBranch::max_weight_of_nudge::<T, I>())]
642 pub fn nudge_referendum(
643 origin: OriginFor<T>,
644 index: ReferendumIndex,
645 ) -> DispatchResultWithPostInfo {
646 ensure_root(origin)?;
647 let now = T::BlockNumberProvider::current_block_number();
648 let mut status = Self::ensure_ongoing(index)?;
649 status.alarm = None;
651 let (info, dirty, branch) = Self::service_referendum(now, index, status);
652 if dirty {
653 ReferendumInfoFor::<T, I>::insert(index, info);
654 }
655 Ok(Some(branch.weight_of_nudge::<T, I>()).into())
656 }
657
658 #[pallet::call_index(6)]
668 #[pallet::weight(OneFewerDecidingBranch::max_weight::<T, I>())]
669 pub fn one_fewer_deciding(
670 origin: OriginFor<T>,
671 track: TrackIdOf<T, I>,
672 ) -> DispatchResultWithPostInfo {
673 ensure_root(origin)?;
674 let track_info = T::Tracks::info(track).ok_or(Error::<T, I>::BadTrack)?;
675 let mut track_queue = TrackQueue::<T, I>::get(track);
676 let branch =
677 if let Some((index, mut status)) = Self::next_for_deciding(&mut track_queue) {
678 let now = T::BlockNumberProvider::current_block_number();
679 let (maybe_alarm, branch) =
680 Self::begin_deciding(&mut status, index, now, &track_info);
681 if let Some(set_alarm) = maybe_alarm {
682 Self::ensure_alarm_at(&mut status, index, set_alarm);
683 }
684 ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status));
685 TrackQueue::<T, I>::insert(track, track_queue);
686 branch.into()
687 } else {
688 DecidingCount::<T, I>::mutate(track, |x| x.saturating_dec());
689 OneFewerDecidingBranch::QueueEmpty
690 };
691 Ok(Some(branch.weight::<T, I>()).into())
692 }
693
694 #[pallet::call_index(7)]
702 #[pallet::weight(T::WeightInfo::refund_submission_deposit())]
703 pub fn refund_submission_deposit(
704 origin: OriginFor<T>,
705 index: ReferendumIndex,
706 ) -> DispatchResult {
707 ensure_signed_or_root(origin)?;
708 let mut info =
709 ReferendumInfoFor::<T, I>::get(index).ok_or(Error::<T, I>::BadReferendum)?;
710 let deposit = info
711 .take_submission_deposit()
712 .map_err(|_| Error::<T, I>::BadStatus)?
713 .ok_or(Error::<T, I>::NoDeposit)?;
714 Self::refund_deposit(Some(deposit.clone()));
715 ReferendumInfoFor::<T, I>::insert(index, info);
716 let e = Event::<T, I>::SubmissionDepositRefunded {
717 index,
718 who: deposit.who,
719 amount: deposit.amount,
720 };
721 Self::deposit_event(e);
722 Ok(())
723 }
724
725 #[pallet::call_index(8)]
733 #[pallet::weight(
734 maybe_hash.map_or(
735 T::WeightInfo::clear_metadata(), |_| T::WeightInfo::set_some_metadata())
736 )]
737 pub fn set_metadata(
738 origin: OriginFor<T>,
739 index: ReferendumIndex,
740 maybe_hash: Option<T::Hash>,
741 ) -> DispatchResult {
742 let who = ensure_signed(origin)?;
743 if let Some(hash) = maybe_hash {
744 let status = Self::ensure_ongoing(index)?;
745 ensure!(status.submission_deposit.who == who, Error::<T, I>::NoPermission);
746 ensure!(T::Preimages::len(&hash).is_some(), Error::<T, I>::PreimageNotExist);
747 MetadataOf::<T, I>::insert(index, hash);
748 Self::deposit_event(Event::<T, I>::MetadataSet { index, hash });
749 Ok(())
750 } else {
751 if let Some(status) = Self::ensure_ongoing(index).ok() {
752 ensure!(status.submission_deposit.who == who, Error::<T, I>::NoPermission);
753 }
754 Self::do_clear_metadata(index);
755 Ok(())
756 }
757 }
758 }
759}
760
761impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> {
762 type Index = ReferendumIndex;
763 type Votes = VotesOf<T, I>;
764 type Moment = BlockNumberFor<T, I>;
765 type Class = TrackIdOf<T, I>;
766
767 fn classes() -> Vec<Self::Class> {
768 T::Tracks::track_ids().collect()
769 }
770
771 fn access_poll<R>(
772 index: Self::Index,
773 f: impl FnOnce(PollStatus<&mut T::Tally, BlockNumberFor<T, I>, TrackIdOf<T, I>>) -> R,
774 ) -> R {
775 match ReferendumInfoFor::<T, I>::get(index) {
776 Some(ReferendumInfo::Ongoing(mut status)) => {
777 let result = f(PollStatus::Ongoing(&mut status.tally, status.track));
778 let now = T::BlockNumberProvider::current_block_number();
779 Self::ensure_alarm_at(&mut status, index, now + One::one());
780 ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status));
781 result
782 },
783 Some(ReferendumInfo::Approved(end, ..)) => f(PollStatus::Completed(end, true)),
784 Some(ReferendumInfo::Rejected(end, ..)) => f(PollStatus::Completed(end, false)),
785 _ => f(PollStatus::None),
786 }
787 }
788
789 fn try_access_poll<R>(
790 index: Self::Index,
791 f: impl FnOnce(
792 PollStatus<&mut T::Tally, BlockNumberFor<T, I>, TrackIdOf<T, I>>,
793 ) -> Result<R, DispatchError>,
794 ) -> Result<R, DispatchError> {
795 match ReferendumInfoFor::<T, I>::get(index) {
796 Some(ReferendumInfo::Ongoing(mut status)) => {
797 let result = f(PollStatus::Ongoing(&mut status.tally, status.track))?;
798 let now = T::BlockNumberProvider::current_block_number();
799 Self::ensure_alarm_at(&mut status, index, now + One::one());
800 ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status));
801 Ok(result)
802 },
803 Some(ReferendumInfo::Approved(end, ..)) => f(PollStatus::Completed(end, true)),
804 Some(ReferendumInfo::Rejected(end, ..)) => f(PollStatus::Completed(end, false)),
805 _ => f(PollStatus::None),
806 }
807 }
808
809 fn as_ongoing(index: Self::Index) -> Option<(T::Tally, TrackIdOf<T, I>)> {
810 Self::ensure_ongoing(index).ok().map(|x| (x.tally, x.track))
811 }
812
813 #[cfg(feature = "runtime-benchmarks")]
814 fn create_ongoing(class: Self::Class) -> Result<Self::Index, ()> {
815 let index = ReferendumCount::<T, I>::mutate(|x| {
816 let r = *x;
817 *x += 1;
818 r
819 });
820 let now = T::BlockNumberProvider::current_block_number();
821 let dummy_account_id =
822 codec::Decode::decode(&mut sp_runtime::traits::TrailingZeroInput::new(&b"dummy"[..]))
823 .expect("infinite length input; no invalid inputs for type; qed");
824 let mut status = ReferendumStatusOf::<T, I> {
825 track: class,
826 origin: frame_support::dispatch::RawOrigin::Root.into(),
827 proposal: T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index }))
828 .map_err(|_| ())?,
829 enactment: DispatchTime::After(Zero::zero()),
830 submitted: now,
831 submission_deposit: Deposit { who: dummy_account_id, amount: Zero::zero() },
832 decision_deposit: None,
833 deciding: None,
834 tally: TallyOf::<T, I>::new(class),
835 in_queue: false,
836 alarm: None,
837 };
838
839 Self::ensure_alarm_at(&mut status, index, sp_runtime::traits::Bounded::max_value());
840 ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status));
841 Ok(index)
842 }
843
844 #[cfg(feature = "runtime-benchmarks")]
845 fn end_ongoing(index: Self::Index, approved: bool) -> Result<(), ()> {
846 let mut status = Self::ensure_ongoing(index).map_err(|_| ())?;
847 Self::ensure_no_alarm(&mut status);
848 Self::note_one_fewer_deciding(status.track);
849 let now = T::BlockNumberProvider::current_block_number();
850 let info = if approved {
851 ReferendumInfo::Approved(now, Some(status.submission_deposit), status.decision_deposit)
852 } else {
853 ReferendumInfo::Rejected(now, Some(status.submission_deposit), status.decision_deposit)
854 };
855 ReferendumInfoFor::<T, I>::insert(index, info);
856 Ok(())
857 }
858
859 #[cfg(feature = "runtime-benchmarks")]
860 fn max_ongoing() -> (Self::Class, u32) {
861 let r = T::Tracks::tracks()
862 .max_by_key(|t| t.info.max_deciding)
863 .expect("Always one class");
864 (r.id, r.info.max_deciding)
865 }
866}
867
868impl<T: Config<I>, I: 'static> Pallet<T, I> {
869 pub fn ensure_ongoing(
872 index: ReferendumIndex,
873 ) -> Result<ReferendumStatusOf<T, I>, DispatchError> {
874 match ReferendumInfoFor::<T, I>::get(index) {
875 Some(ReferendumInfo::Ongoing(status)) => Ok(status),
876 _ => Err(Error::<T, I>::NotOngoing.into()),
877 }
878 }
879
880 pub fn is_referendum_passing(ref_index: ReferendumIndex) -> Result<bool, DispatchError> {
883 let info = ReferendumInfoFor::<T, I>::get(ref_index).ok_or(Error::<T, I>::BadReferendum)?;
884 match info {
885 ReferendumInfo::Ongoing(status) => {
886 let track = T::Tracks::info(status.track).ok_or(Error::<T, I>::NoTrack)?;
887 let elapsed = if let Some(deciding) = status.deciding {
888 T::BlockNumberProvider::current_block_number().saturating_sub(deciding.since)
889 } else {
890 Zero::zero()
891 };
892 Ok(Self::is_passing(
893 &status.tally,
894 elapsed,
895 track.decision_period,
896 &track.min_support,
897 &track.min_approval,
898 status.track,
899 ))
900 },
901 _ => Err(Error::<T, I>::NotOngoing.into()),
902 }
903 }
904
905 fn schedule_enactment(
907 index: ReferendumIndex,
908 track: &TrackInfoOf<T, I>,
909 desired: DispatchTime<BlockNumberFor<T, I>>,
910 origin: PalletsOriginOf<T>,
911 call: BoundedCallOf<T, I>,
912 ) {
913 let now = T::BlockNumberProvider::current_block_number();
914 let earliest_allowed = now.saturating_add(track.min_enactment_period.max(One::one()));
916 let desired = desired.evaluate(now);
917 let ok = T::Scheduler::schedule_named(
918 (ASSEMBLY_ID, "enactment", index).using_encoded(sp_io::hashing::blake2_256),
919 DispatchTime::At(desired.max(earliest_allowed)),
920 None,
921 63,
922 origin,
923 call,
924 )
925 .is_ok();
926 debug_assert!(ok, "LOGIC ERROR: bake_referendum/schedule_named failed");
927 }
928
929 fn set_alarm(
931 call: BoundedCallOf<T, I>,
932 when: BlockNumberFor<T, I>,
933 ) -> Option<(BlockNumberFor<T, I>, ScheduleAddressOf<T, I>)> {
934 let alarm_interval = T::AlarmInterval::get().max(One::one());
935 let when = (when.saturating_add(alarm_interval.saturating_sub(One::one())) /
938 alarm_interval)
939 .saturating_mul(alarm_interval);
940 let result = T::Scheduler::schedule(
941 DispatchTime::At(when),
942 None,
943 128u8,
944 frame_system::RawOrigin::Root.into(),
945 call,
946 );
947 debug_assert!(
948 result.is_ok(),
949 "Unable to schedule a new alarm at #{:?} (now: #{:?}), scheduler error: `{:?}`",
950 when,
951 T::BlockNumberProvider::current_block_number(),
952 result.unwrap_err(),
953 );
954 result.ok().map(|x| (when, x))
955 }
956
957 fn begin_deciding(
964 status: &mut ReferendumStatusOf<T, I>,
965 index: ReferendumIndex,
966 now: BlockNumberFor<T, I>,
967 track: &TrackInfoOf<T, I>,
968 ) -> (Option<BlockNumberFor<T, I>>, BeginDecidingBranch) {
969 let is_passing = Self::is_passing(
970 &status.tally,
971 Zero::zero(),
972 track.decision_period,
973 &track.min_support,
974 &track.min_approval,
975 status.track,
976 );
977 status.in_queue = false;
978 Self::deposit_event(Event::<T, I>::DecisionStarted {
979 index,
980 tally: status.tally.clone(),
981 proposal: status.proposal.clone(),
982 track: status.track,
983 });
984 let confirming = if is_passing {
985 Self::deposit_event(Event::<T, I>::ConfirmStarted { index });
986 Some(now.saturating_add(track.confirm_period))
987 } else {
988 None
989 };
990 let deciding_status = DecidingStatus { since: now, confirming };
991 let alarm = Self::decision_time(&deciding_status, &status.tally, status.track, track)
992 .max(now.saturating_add(One::one()));
993 status.deciding = Some(deciding_status);
994 let branch =
995 if is_passing { BeginDecidingBranch::Passing } else { BeginDecidingBranch::Failing };
996 (Some(alarm), branch)
997 }
998
999 fn ready_for_deciding(
1004 now: BlockNumberFor<T, I>,
1005 track: &TrackInfoOf<T, I>,
1006 index: ReferendumIndex,
1007 status: &mut ReferendumStatusOf<T, I>,
1008 ) -> (Option<BlockNumberFor<T, I>>, ServiceBranch) {
1009 let deciding_count = DecidingCount::<T, I>::get(status.track);
1010 if deciding_count < track.max_deciding {
1011 DecidingCount::<T, I>::insert(status.track, deciding_count.saturating_add(1));
1013 let r = Self::begin_deciding(status, index, now, track);
1014 (r.0, r.1.into())
1015 } else {
1016 let item = (index, status.tally.ayes(status.track));
1018 status.in_queue = true;
1019 TrackQueue::<T, I>::mutate(status.track, |q| q.insert_sorted_by_key(item, |x| x.1));
1020 (None, ServiceBranch::Queued)
1021 }
1022 }
1023
1024 fn next_for_deciding(
1027 track_queue: &mut BoundedVec<(u32, VotesOf<T, I>), T::MaxQueued>,
1028 ) -> Option<(ReferendumIndex, ReferendumStatusOf<T, I>)> {
1029 loop {
1030 let (index, _) = track_queue.pop()?;
1031 match Self::ensure_ongoing(index) {
1032 Ok(s) => return Some((index, s)),
1033 Err(_) => {}, }
1035 }
1036 }
1037
1038 fn note_one_fewer_deciding(track: TrackIdOf<T, I>) {
1042 let now = T::BlockNumberProvider::current_block_number();
1044 let next_block = now + One::one();
1045 let call = match T::Preimages::bound(CallOf::<T, I>::from(Call::one_fewer_deciding {
1046 track,
1047 })) {
1048 Ok(c) => c,
1049 Err(_) => {
1050 debug_assert!(false, "Unable to create a bounded call from `one_fewer_deciding`??",);
1051 return;
1052 },
1053 };
1054 Self::set_alarm(call, next_block);
1055 }
1056
1057 fn ensure_alarm_at(
1063 status: &mut ReferendumStatusOf<T, I>,
1064 index: ReferendumIndex,
1065 alarm: BlockNumberFor<T, I>,
1066 ) -> bool {
1067 if status.alarm.as_ref().map_or(true, |&(when, _)| when != alarm) {
1068 Self::ensure_no_alarm(status);
1070 let call =
1071 match T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index })) {
1072 Ok(c) => c,
1073 Err(_) => {
1074 debug_assert!(
1075 false,
1076 "Unable to create a bounded call from `nudge_referendum`??",
1077 );
1078 return false;
1079 },
1080 };
1081 status.alarm = Self::set_alarm(call, alarm);
1082 true
1083 } else {
1084 false
1085 }
1086 }
1087
1088 fn service_referendum(
1110 now: BlockNumberFor<T, I>,
1111 index: ReferendumIndex,
1112 mut status: ReferendumStatusOf<T, I>,
1113 ) -> (ReferendumInfoOf<T, I>, bool, ServiceBranch) {
1114 let mut dirty = false;
1115 let track = match T::Tracks::info(status.track) {
1117 Some(x) => x,
1118 None => return (ReferendumInfo::Ongoing(status), false, ServiceBranch::Fail),
1119 };
1120 let timeout = status.submitted + T::UndecidingTimeout::get();
1122 let mut alarm = BlockNumberFor::<T, I>::max_value();
1123 let branch;
1124 match &mut status.deciding {
1125 None => {
1126 if status.in_queue {
1128 let ayes = status.tally.ayes(status.track);
1130 let mut queue = TrackQueue::<T, I>::get(status.track);
1131 let maybe_old_pos = queue.iter().position(|(x, _)| *x == index);
1132 let new_pos = queue.binary_search_by_key(&ayes, |x| x.1).unwrap_or_else(|x| x);
1133 branch = if maybe_old_pos.is_none() && new_pos > 0 {
1134 let _ = queue.force_insert_keep_right(new_pos, (index, ayes));
1136 ServiceBranch::RequeuedInsertion
1137 } else if let Some(old_pos) = maybe_old_pos {
1138 queue[old_pos].1 = ayes;
1140 queue.slide(old_pos, new_pos);
1141 ServiceBranch::RequeuedSlide
1142 } else {
1143 ServiceBranch::NotQueued
1144 };
1145 TrackQueue::<T, I>::insert(status.track, queue);
1146 } else {
1147 branch = if status.decision_deposit.is_some() {
1149 let prepare_end = status.submitted.saturating_add(track.prepare_period);
1150 if now >= prepare_end {
1151 let (maybe_alarm, branch) =
1152 Self::ready_for_deciding(now, &track, index, &mut status);
1153 if let Some(set_alarm) = maybe_alarm {
1154 alarm = alarm.min(set_alarm);
1155 }
1156 dirty = true;
1157 branch
1158 } else {
1159 alarm = alarm.min(prepare_end);
1160 ServiceBranch::Preparing
1161 }
1162 } else {
1163 alarm = timeout;
1164 ServiceBranch::NoDeposit
1165 }
1166 }
1167 if status.deciding.is_none() && now >= timeout && !status.in_queue {
1169 Self::ensure_no_alarm(&mut status);
1171 Self::deposit_event(Event::<T, I>::TimedOut { index, tally: status.tally });
1172 return (
1173 ReferendumInfo::TimedOut(
1174 now,
1175 Some(status.submission_deposit),
1176 status.decision_deposit,
1177 ),
1178 true,
1179 ServiceBranch::TimedOut,
1180 );
1181 }
1182 },
1183 Some(deciding) => {
1184 let is_passing = Self::is_passing(
1185 &status.tally,
1186 now.saturating_sub(deciding.since),
1187 track.decision_period,
1188 &track.min_support,
1189 &track.min_approval,
1190 status.track,
1191 );
1192 branch = if is_passing {
1193 match deciding.confirming {
1194 Some(t) if now >= t => {
1195 Self::ensure_no_alarm(&mut status);
1197 Self::note_one_fewer_deciding(status.track);
1198 let (desired, call) = (status.enactment, status.proposal);
1199 Self::schedule_enactment(index, &track, desired, status.origin, call);
1200 Self::deposit_event(Event::<T, I>::Confirmed {
1201 index,
1202 tally: status.tally,
1203 });
1204 return (
1205 ReferendumInfo::Approved(
1206 now,
1207 Some(status.submission_deposit),
1208 status.decision_deposit,
1209 ),
1210 true,
1211 ServiceBranch::Approved,
1212 );
1213 },
1214 Some(_) => ServiceBranch::ContinueConfirming,
1215 None => {
1216 dirty = true;
1218 deciding.confirming = Some(now.saturating_add(track.confirm_period));
1219 Self::deposit_event(Event::<T, I>::ConfirmStarted { index });
1220 ServiceBranch::BeginConfirming
1221 },
1222 }
1223 } else {
1224 if now >= deciding.since.saturating_add(track.decision_period) {
1225 Self::ensure_no_alarm(&mut status);
1227 Self::note_one_fewer_deciding(status.track);
1228 Self::deposit_event(Event::<T, I>::Rejected { index, tally: status.tally });
1229 return (
1230 ReferendumInfo::Rejected(
1231 now,
1232 Some(status.submission_deposit),
1233 status.decision_deposit,
1234 ),
1235 true,
1236 ServiceBranch::Rejected,
1237 );
1238 }
1239 if deciding.confirming.is_some() {
1240 dirty = true;
1242 deciding.confirming = None;
1243 Self::deposit_event(Event::<T, I>::ConfirmAborted { index });
1244 ServiceBranch::EndConfirming
1245 } else {
1246 ServiceBranch::ContinueNotConfirming
1247 }
1248 };
1249 alarm = Self::decision_time(deciding, &status.tally, status.track, &track);
1250 },
1251 }
1252
1253 let dirty_alarm = if alarm < BlockNumberFor::<T, I>::max_value() {
1254 Self::ensure_alarm_at(&mut status, index, alarm)
1255 } else {
1256 Self::ensure_no_alarm(&mut status)
1257 };
1258 (ReferendumInfo::Ongoing(status), dirty_alarm || dirty, branch)
1259 }
1260
1261 fn decision_time(
1264 deciding: &DecidingStatusOf<T, I>,
1265 tally: &T::Tally,
1266 track_id: TrackIdOf<T, I>,
1267 track: &TrackInfoOf<T, I>,
1268 ) -> BlockNumberFor<T, I> {
1269 deciding.confirming.unwrap_or_else(|| {
1270 let approval = tally.approval(track_id);
1272 let support = tally.support(track_id);
1273 let until_approval = track.min_approval.delay(approval);
1274 let until_support = track.min_support.delay(support);
1275 let offset = until_support.max(until_approval);
1276 deciding.since.saturating_add(offset.mul_ceil(track.decision_period))
1277 })
1278 }
1279
1280 fn ensure_no_alarm(status: &mut ReferendumStatusOf<T, I>) -> bool {
1282 if let Some((_, last_alarm)) = status.alarm.take() {
1283 let _ = T::Scheduler::cancel(last_alarm);
1285 true
1286 } else {
1287 false
1288 }
1289 }
1290
1291 fn take_deposit(
1293 who: T::AccountId,
1294 amount: BalanceOf<T, I>,
1295 ) -> Result<Deposit<T::AccountId, BalanceOf<T, I>>, DispatchError> {
1296 T::Currency::reserve(&who, amount)?;
1297 Ok(Deposit { who, amount })
1298 }
1299
1300 fn refund_deposit(deposit: Option<Deposit<T::AccountId, BalanceOf<T, I>>>) {
1302 if let Some(Deposit { who, amount }) = deposit {
1303 T::Currency::unreserve(&who, amount);
1304 }
1305 }
1306
1307 fn slash_deposit(deposit: Option<Deposit<T::AccountId, BalanceOf<T, I>>>) {
1309 if let Some(Deposit { who, amount }) = deposit {
1310 T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0);
1311 Self::deposit_event(Event::<T, I>::DepositSlashed { who, amount });
1312 }
1313 }
1314
1315 fn is_passing(
1319 tally: &T::Tally,
1320 elapsed: BlockNumberFor<T, I>,
1321 period: BlockNumberFor<T, I>,
1322 support_needed: &Curve,
1323 approval_needed: &Curve,
1324 id: TrackIdOf<T, I>,
1325 ) -> bool {
1326 let x = Perbill::from_rational(elapsed.min(period), period);
1327 support_needed.passing(x, tally.support(id)) &&
1328 approval_needed.passing(x, tally.approval(id))
1329 }
1330
1331 fn do_clear_metadata(index: ReferendumIndex) {
1333 if let Some(hash) = MetadataOf::<T, I>::take(index) {
1334 Self::deposit_event(Event::<T, I>::MetadataCleared { index, hash });
1335 }
1336 }
1337
1338 #[cfg(any(feature = "try-runtime", test))]
1348 fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
1349 ensure!(
1350 ReferendumCount::<T, I>::get() as usize ==
1351 ReferendumInfoFor::<T, I>::iter_keys().count(),
1352 "Number of referenda in `ReferendumInfoFor` is different than `ReferendumCount`"
1353 );
1354
1355 MetadataOf::<T, I>::iter_keys().try_for_each(|referendum_index| -> DispatchResult {
1356 ensure!(
1357 ReferendumInfoFor::<T, I>::contains_key(referendum_index),
1358 "Referendum indices in `MetadataOf` must also be stored in `ReferendumInfoOf`"
1359 );
1360 Ok(())
1361 })?;
1362
1363 Self::try_state_referenda_info()?;
1364 Self::try_state_tracks()?;
1365
1366 Ok(())
1367 }
1368
1369 #[cfg(any(feature = "try-runtime", test))]
1378 fn try_state_referenda_info() -> Result<(), sp_runtime::TryRuntimeError> {
1379 ReferendumInfoFor::<T, I>::iter().try_for_each(|(_, referendum)| {
1380 match referendum {
1381 ReferendumInfo::Ongoing(status) => {
1382 ensure!(
1383 T::Tracks::info(status.track).is_some(),
1384 "No track info for the track of the referendum."
1385 );
1386
1387 if let Some(deciding) = status.deciding {
1388 ensure!(
1389 deciding.since <
1390 deciding
1391 .confirming
1392 .unwrap_or(BlockNumberFor::<T, I>::max_value()),
1393 "Deciding status cannot begin before confirming stage."
1394 )
1395 }
1396 },
1397 _ => {},
1398 }
1399 Ok(())
1400 })
1401 }
1402
1403 #[cfg(any(feature = "try-runtime", test))]
1408 fn try_state_tracks() -> Result<(), sp_runtime::TryRuntimeError> {
1409 T::Tracks::tracks().try_for_each(|track| {
1410 TrackQueue::<T, I>::get(track.id).iter().try_for_each(
1411 |(referendum_index, _)| -> Result<(), sp_runtime::TryRuntimeError> {
1412 ensure!(
1413 ReferendumInfoFor::<T, I>::contains_key(referendum_index),
1414 "`ReferendumIndex` inside the `TrackQueue` should be a key in `ReferendumInfoFor`"
1415 );
1416 Ok(())
1417 },
1418 )?;
1419 Ok(())
1420 })
1421 }
1422}