Skip to main content

pezpallet_whitelist/
lib.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Whitelist Pezpallet
19//!
20//! - [`Config`]
21//! - [`Call`]
22//!
23//! ## Overview
24//!
25//! Allow some configurable origin: [`Config::WhitelistOrigin`] to whitelist some hash of a call,
26//! and allow another configurable origin: [`Config::DispatchWhitelistedOrigin`] to dispatch them
27//! with the root origin.
28//!
29//! In the meantime the call corresponding to the hash must have been submitted to the pre-image
30//! handler [`pezpallet::Config::Preimages`].
31
32#![cfg_attr(not(feature = "std"), no_std)]
33
34#[cfg(feature = "runtime-benchmarks")]
35mod benchmarking;
36#[cfg(test)]
37mod mock;
38#[cfg(test)]
39mod tests;
40pub mod weights;
41pub use weights::WeightInfo;
42
43extern crate alloc;
44
45use alloc::boxed::Box;
46use codec::{DecodeLimit, Encode, FullCodec};
47use frame::{
48	prelude::*,
49	traits::{QueryPreimage, StorePreimage},
50};
51use scale_info::TypeInfo;
52
53pub use pezpallet::*;
54
55#[frame::pezpallet]
56pub mod pezpallet {
57	use super::*;
58
59	#[pezpallet::config]
60	pub trait Config: pezframe_system::Config {
61		/// The overarching event type.
62		#[allow(deprecated)]
63		type RuntimeEvent: From<Event<Self>>
64			+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
65
66		/// The overarching call type.
67		type RuntimeCall: IsType<<Self as pezframe_system::Config>::RuntimeCall>
68			+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
69			+ GetDispatchInfo
70			+ FullCodec
71			+ TypeInfo
72			+ From<pezframe_system::Call<Self>>
73			+ Parameter;
74
75		/// Required origin for whitelisting a call.
76		type WhitelistOrigin: EnsureOrigin<Self::RuntimeOrigin>;
77
78		/// Required origin for dispatching whitelisted call with root origin.
79		type DispatchWhitelistedOrigin: EnsureOrigin<Self::RuntimeOrigin>;
80
81		/// The handler of pre-images.
82		type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
83
84		/// The weight information for this pezpallet.
85		type WeightInfo: WeightInfo;
86	}
87
88	#[pezpallet::pezpallet]
89	pub struct Pezpallet<T>(_);
90
91	#[pezpallet::event]
92	#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
93	pub enum Event<T: Config> {
94		CallWhitelisted { call_hash: T::Hash },
95		WhitelistedCallRemoved { call_hash: T::Hash },
96		WhitelistedCallDispatched { call_hash: T::Hash, result: DispatchResultWithPostInfo },
97	}
98
99	#[pezpallet::error]
100	pub enum Error<T> {
101		/// The preimage of the call hash could not be loaded.
102		UnavailablePreImage,
103		/// The call could not be decoded.
104		UndecodableCall,
105		/// The weight of the decoded call was higher than the witness.
106		InvalidCallWeightWitness,
107		/// The call was not whitelisted.
108		CallIsNotWhitelisted,
109		/// The call was already whitelisted; No-Op.
110		CallAlreadyWhitelisted,
111	}
112
113	#[pezpallet::storage]
114	pub type WhitelistedCall<T: Config> = StorageMap<_, Twox64Concat, T::Hash, (), OptionQuery>;
115
116	#[pezpallet::call]
117	impl<T: Config> Pezpallet<T> {
118		#[pezpallet::call_index(0)]
119		#[pezpallet::weight(T::WeightInfo::whitelist_call())]
120		pub fn whitelist_call(origin: OriginFor<T>, call_hash: T::Hash) -> DispatchResult {
121			T::WhitelistOrigin::ensure_origin(origin)?;
122
123			ensure!(
124				!WhitelistedCall::<T>::contains_key(call_hash),
125				Error::<T>::CallAlreadyWhitelisted,
126			);
127
128			WhitelistedCall::<T>::insert(call_hash, ());
129			T::Preimages::request(&call_hash);
130
131			Self::deposit_event(Event::<T>::CallWhitelisted { call_hash });
132
133			Ok(())
134		}
135
136		#[pezpallet::call_index(1)]
137		#[pezpallet::weight(T::WeightInfo::remove_whitelisted_call())]
138		pub fn remove_whitelisted_call(origin: OriginFor<T>, call_hash: T::Hash) -> DispatchResult {
139			T::WhitelistOrigin::ensure_origin(origin)?;
140
141			WhitelistedCall::<T>::take(call_hash).ok_or(Error::<T>::CallIsNotWhitelisted)?;
142
143			T::Preimages::unrequest(&call_hash);
144
145			Self::deposit_event(Event::<T>::WhitelistedCallRemoved { call_hash });
146
147			Ok(())
148		}
149
150		#[pezpallet::call_index(2)]
151		#[pezpallet::weight(
152			T::WeightInfo::dispatch_whitelisted_call(*call_encoded_len)
153				.saturating_add(*call_weight_witness)
154		)]
155		pub fn dispatch_whitelisted_call(
156			origin: OriginFor<T>,
157			call_hash: T::Hash,
158			call_encoded_len: u32,
159			call_weight_witness: Weight,
160		) -> DispatchResultWithPostInfo {
161			T::DispatchWhitelistedOrigin::ensure_origin(origin)?;
162
163			ensure!(
164				WhitelistedCall::<T>::contains_key(call_hash),
165				Error::<T>::CallIsNotWhitelisted,
166			);
167
168			let call = T::Preimages::fetch(&call_hash, Some(call_encoded_len))
169				.map_err(|_| Error::<T>::UnavailablePreImage)?;
170
171			let call = <T as Config>::RuntimeCall::decode_all_with_depth_limit(
172				frame::deps::pezframe_support::MAX_EXTRINSIC_DEPTH,
173				&mut &call[..],
174			)
175			.map_err(|_| Error::<T>::UndecodableCall)?;
176
177			ensure!(
178				call.get_dispatch_info().call_weight.all_lte(call_weight_witness),
179				Error::<T>::InvalidCallWeightWitness
180			);
181
182			let actual_weight = Self::clean_and_dispatch(call_hash, call).map(|w| {
183				w.saturating_add(T::WeightInfo::dispatch_whitelisted_call(call_encoded_len))
184			});
185
186			Ok(actual_weight.into())
187		}
188
189		#[pezpallet::call_index(3)]
190		#[pezpallet::weight({
191			let call_weight = call.get_dispatch_info().call_weight;
192			let call_len = call.encoded_size() as u32;
193
194			T::WeightInfo::dispatch_whitelisted_call_with_preimage(call_len)
195				.saturating_add(call_weight)
196		})]
197		pub fn dispatch_whitelisted_call_with_preimage(
198			origin: OriginFor<T>,
199			call: Box<<T as Config>::RuntimeCall>,
200		) -> DispatchResultWithPostInfo {
201			T::DispatchWhitelistedOrigin::ensure_origin(origin)?;
202
203			let call_hash = T::Hashing::hash_of(&call).into();
204
205			ensure!(
206				WhitelistedCall::<T>::contains_key(call_hash),
207				Error::<T>::CallIsNotWhitelisted,
208			);
209
210			let call_len = call.encoded_size() as u32;
211			let actual_weight = Self::clean_and_dispatch(call_hash, *call).map(|w| {
212				w.saturating_add(T::WeightInfo::dispatch_whitelisted_call_with_preimage(call_len))
213			});
214
215			Ok(actual_weight.into())
216		}
217	}
218}
219
220impl<T: Config> Pezpallet<T> {
221	/// Clean whitelisting/preimage and dispatch call.
222	///
223	/// Return the call actual weight of the dispatched call if there is some.
224	fn clean_and_dispatch(call_hash: T::Hash, call: <T as Config>::RuntimeCall) -> Option<Weight> {
225		WhitelistedCall::<T>::remove(call_hash);
226
227		T::Preimages::unrequest(&call_hash);
228
229		let result = call.dispatch(pezframe_system::Origin::<T>::Root.into());
230
231		let call_actual_weight = match result {
232			Ok(call_post_info) => call_post_info.actual_weight,
233			Err(call_err) => call_err.post_info.actual_weight,
234		};
235
236		Self::deposit_event(Event::<T>::WhitelistedCallDispatched { call_hash, result });
237
238		call_actual_weight
239	}
240}