From 938e4edf86f762bb3495f17ac67564e18623551d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 21 Dec 2023 19:40:55 -0500 Subject: [PATCH] feat(referenda-tracks): define pallet calls --- substrate/frame/referenda-tracks/src/lib.rs | 190 +++++++++++++----- .../frame/referenda-tracks/src/weights.rs | 56 +++++- 2 files changed, 191 insertions(+), 55 deletions(-) diff --git a/substrate/frame/referenda-tracks/src/lib.rs b/substrate/frame/referenda-tracks/src/lib.rs index ad5bdaf9dff80..648bdde7a00e6 100644 --- a/substrate/frame/referenda-tracks/src/lib.rs +++ b/substrate/frame/referenda-tracks/src/lib.rs @@ -15,10 +15,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Edit and manage referenda voring tracks. +//! # Referenda Tracks Pallet +//! +//! - [`Config`][Config] +//! - [`Call`][Call] +//! +//! ## Overview +//! +//! Manage referenda voting tracks. +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! - [`insert`][`crate::Pallet::insert`] - Insert a new referenda Track. +//! - [`update`][`crate::Pallet::update`] - Update the configuration of an existing referenda Track. +//! - [`remove`][`crate::Pallet::remove`] - Remove an existing track + #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +mod impl_tracks_info; pub mod weights; #[cfg(test)] @@ -29,13 +46,14 @@ mod tests; use core::iter::Map; use frame_support::{storage::PrefixIterator, traits::OriginTrait}; use frame_system::pallet_prelude::BlockNumberFor; -use pallet_referenda::{BalanceOf, PalletsOriginOf, Track, TrackIdOf, TrackInfoOf}; +use pallet_referenda::{BalanceOf, PalletsOriginOf, Track, TrackInfoOf}; use sp_core::Get; use sp_std::{borrow::Cow, vec::Vec}; pub use pallet::*; pub use weights::WeightInfo; +type TrackIdOf = >::TrackId; type TrackOf = Track<>::TrackId, BalanceOf, BlockNumberFor>; type TracksIter = Map< @@ -43,85 +61,157 @@ type TracksIter = Map< fn((>::TrackId, TrackInfoOf)) -> Cow<'static, TrackOf>, >; -impl, I> pallet_referenda::TracksInfo, BlockNumberFor> - for Pallet -{ - type Id = T::TrackId; - type RuntimeOrigin = ::PalletsOrigin; - type TracksIter = TracksIter; - - fn tracks() -> Self::TracksIter { - Tracks::::iter().map(|(id, info)| Cow::Owned(Track { id, info })) - } - fn track_for(origin: &Self::RuntimeOrigin) -> Result { - OriginToTrackId::::get(origin).ok_or(()) - } - fn tracks_ids() -> Vec { - TracksIds::::get().into_inner() - } - fn info(id: Self::Id) -> Option>> { - Tracks::::get(id).map(Cow::Owned) - } -} - -impl, I: 'static> Get>> for crate::Pallet { - fn get() -> Vec, BlockNumberFor>> { - // expensive but it doesn't seem to be used anywhere - as pallet_referenda::TracksInfo, BlockNumberFor>>::tracks() - .map(|t| t.into_owned()) - .collect() - } -} - #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config + pallet_referenda::Config { + type UpdateOrigin: EnsureOrigin; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; type TrackId: Parameter + Member + Copy + MaxEncodedLen + Ord; type MaxTracks: Get; - // type WeightInfo: WeightInfo; + /// + type WeightInfo: WeightInfo; } #[pallet::pallet] pub struct Pallet(_); - #[pallet::call] - impl, I: 'static> Pallet { - #[pallet::call_index(0)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] - pub fn udpate(origin: OriginFor, _id: TrackIdOf) -> DispatchResultWithPostInfo { - let _sender = ensure_signed(origin)?; - // Self::deposit_event(Event::Foo { sender }); - Ok(().into()) - } - } - #[pallet::storage] pub type TracksIds, I: 'static = ()> = - StorageValue<_, BoundedVec>::MaxTracks>, ValueQuery>; + StorageValue<_, BoundedVec, >::MaxTracks>, ValueQuery>; #[pallet::storage] pub type OriginToTrackId, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, PalletsOriginOf, T::TrackId>; + StorageMap<_, Blake2_128Concat, PalletsOriginOf, TrackIdOf>; #[pallet::storage] pub type Tracks, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::TrackId, TrackInfoOf>; + StorageMap<_, Blake2_128Concat, TrackIdOf, TrackInfoOf>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { - // Foo(T::AccountId), + /// A new track has been inserted + Created { id: TrackIdOf }, + /// The information for a track has been updated + Updated { id: TrackIdOf, info: TrackInfoOf }, + /// A track has been removed + Removed { id: TrackIdOf }, } #[pallet::error] - pub enum Error {} + pub enum Error { + /// The maxmimum amount of track IDs was exceeded + MaxTracksExceeded, + /// The specified ID for this track was not found + TrackIdNotFound, + /// The specified ID for this track was already existing + TrackIdAlreadyExisting, + /// The track cannot be removed + CannotRemove, + } + + #[pallet::call(weight(>::WeightInfo))] + impl, I: 'static> Pallet { + /// Insert a new referenda Track. + /// + /// Parameters: + /// - `id`: The Id of the track to be inserted. + /// - `info`: The configuration of the track. + /// - `pallet_origin`: A generic origin that will be matched to the track. + /// + /// Emits `Created` event when successful. + /// + /// Weight: `O(1)` + #[pallet::call_index(0)] + pub fn insert( + origin: OriginFor, + id: TrackIdOf, + info: TrackInfoOf, + pallet_origin: PalletsOriginOf, + ) -> DispatchResultWithPostInfo { + T::UpdateOrigin::ensure_origin(origin)?; + + ensure!(Tracks::::get(id) == None, Error::::TrackIdAlreadyExisting); + + TracksIds::::try_append(id).map_err(|_| Error::::MaxTracksExceeded)?; + Tracks::::set(id, Some(info)); + OriginToTrackId::::set(pallet_origin.clone(), Some(id)); + + Self::deposit_event(Event::Created { id }); + Ok(().into()) + } + + /// Update the configuration of an existing referenda Track. + /// + /// Parameters: + /// - `id`: The Id of the track to be updated. + /// - `info`: The new configuration of the track. + /// + /// Emits `Updated` event when successful. + /// + /// Weight: `O(1)` + #[pallet::call_index(1)] + pub fn update( + origin: OriginFor, + id: TrackIdOf, + info: TrackInfoOf, + ) -> DispatchResultWithPostInfo { + T::UpdateOrigin::ensure_origin(origin)?; + + Tracks::::try_mutate(id, |track| { + if track.is_none() { + return Err(Error::::TrackIdNotFound); + }; + + *track = Some(info.clone()); + + Ok(()) + })?; + + Self::deposit_event(Event::Updated { id, info }); + Ok(().into()) + } + + /// Remove an existing track + /// + /// Parameters: + /// - `id`: The Id of the track to be deleted. + /// + /// Emits `Removed` event when successful. + /// + /// Weight: `O(n)` + #[pallet::call_index(2)] + pub fn remove(origin: OriginFor, id: TrackIdOf) -> DispatchResultWithPostInfo { + T::UpdateOrigin::ensure_origin(origin)?; + + ensure!(Tracks::::contains_key(id), Error::::TrackIdNotFound); + + OriginToTrackId::::iter_keys().for_each(|origin| { + let track_id = OriginToTrackId::::get(origin.clone()) + .expect("The given key is provided by StorageMap::iter_keys; qed"); + if track_id == id { + OriginToTrackId::::remove(origin); + } + }); + Tracks::::remove(id); + TracksIds::::try_mutate(|tracks_ids| { + let new_tracks_ids = tracks_ids.clone().into_iter().filter(|i| i != &id).collect(); + *tracks_ids = BoundedVec::<_, _>::truncate_from(new_tracks_ids); + + Ok::<(), DispatchError>(()) + })?; + + Self::deposit_event(Event::Removed { id }); + Ok(().into()) + } + } } diff --git a/substrate/frame/referenda-tracks/src/weights.rs b/substrate/frame/referenda-tracks/src/weights.rs index 46475db163ffd..61e068846157b 100644 --- a/substrate/frame/referenda-tracks/src/weights.rs +++ b/substrate/frame/referenda-tracks/src/weights.rs @@ -52,34 +52,80 @@ use core::marker::PhantomData; /// Weight functions needed for pallet_remark. pub trait WeightInfo { - fn store(l: u32, ) -> Weight; + fn insert() -> Weight; + fn update() -> Weight; + fn remove() -> Weight; } /// Weights for pallet_remark using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[1, 1048576]`. - fn store(l: u32, ) -> Weight { + fn insert() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 8_471_000 picoseconds. Weight::from_parts(8_586_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_359, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn update() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn remove() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) } } // For backwards compatibility and tests impl WeightInfo for () { /// The range of component `l` is `[1, 1048576]`. - fn store(l: u32, ) -> Weight { + fn insert() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn update() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_471_000 picoseconds. + Weight::from_parts(8_586_000, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_359, 0)) + } + + /// The range of component `l` is `[1, 1048576]`. + fn remove() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 8_471_000 picoseconds. Weight::from_parts(8_586_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_359, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_359, 0)) } }