pallet_handles/
handles_signed_extension.rs1use crate::{Call, Config, Error, MSAIdToDisplayName, WeightInfo};
3use common_primitives::msa::MsaValidator;
4use core::marker::PhantomData;
5use frame_support::{
6 dispatch::DispatchInfo, ensure, pallet_prelude::ValidTransaction, traits::IsSubType,
7 unsigned::UnknownTransaction, RuntimeDebugNoBound,
8};
9use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode};
10use scale_info::TypeInfo;
11use sp_runtime::{
12 traits::{
13 AsSystemOriginSigner, DispatchInfoOf, Dispatchable, PostDispatchInfoOf,
14 TransactionExtension,
15 },
16 transaction_validity::{
17 InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
18 },
19 DispatchError, DispatchResult, ModuleError, Weight,
20};
21
22#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
26#[scale_info(skip_type_params(T))]
27pub struct HandlesSignedExtension<T: Config + Send + Sync>(PhantomData<T>);
28
29impl<T: Config + Send + Sync> HandlesSignedExtension<T> {
30 pub fn new() -> Self {
32 Self(core::marker::PhantomData)
33 }
34
35 pub fn validate_retire_handle(delegator_key: &T::AccountId) -> TransactionValidity {
46 const TAG_PREFIX: &str = "HandlesRetireHandle";
47
48 let delegator_msa_id =
50 T::MsaInfoProvider::ensure_valid_msa_key(delegator_key).map_err(map_dispatch_error)?;
51 let handle_from_state = MSAIdToDisplayName::<T>::try_get(delegator_msa_id)
53 .map_err(|_| UnknownTransaction::CannotLookup)?;
54 let expiration = handle_from_state.1;
55 let current_block = frame_system::Pallet::<T>::block_number();
56
57 let is_past_min_lifetime = current_block >= expiration;
58 ensure!(
59 is_past_min_lifetime,
60 map_dispatch_error(DispatchError::Other(
61 Error::<T>::HandleWithinMortalityPeriod.into()
62 ))
63 );
64
65 ValidTransaction::with_tag_prefix(TAG_PREFIX).build()
66 }
67}
68
69impl<T: Config + Send + Sync> core::fmt::Debug for HandlesSignedExtension<T> {
70 #[cfg(feature = "std")]
71 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
72 write!(f, "HandlesSignedExtension<{:?}>", self.0)
73 }
74 #[cfg(not(feature = "std"))]
75 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
76 Ok(())
77 }
78}
79
80pub fn map_dispatch_error(err: DispatchError) -> InvalidTransaction {
82 InvalidTransaction::Custom(match err {
83 DispatchError::Module(ModuleError { error, .. }) => error[0],
84 _ => 255u8,
85 })
86}
87
88#[derive(RuntimeDebugNoBound)]
90pub enum Val {
91 Valid,
93 Refund(Weight),
95}
96
97#[derive(RuntimeDebugNoBound)]
99pub enum Pre {
100 Valid,
102 Refund(Weight),
104}
105
106impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for HandlesSignedExtension<T>
107where
108 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + IsSubType<Call<T>>,
109 <T as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
110{
111 const IDENTIFIER: &'static str = "HandlesSignedExtension";
112 type Implicit = ();
113 type Val = Val;
114 type Pre = Pre;
115
116 fn weight(&self, call: &T::RuntimeCall) -> Weight {
117 if let Some(Call::retire_handle {}) = call.is_sub_type() {
118 T::WeightInfo::validate_retire_handle_benchmark()
119 } else {
120 Weight::zero()
121 }
122 }
123 fn validate(
135 &self,
136 origin: <T as frame_system::Config>::RuntimeOrigin,
137 call: &T::RuntimeCall,
138 _info: &DispatchInfoOf<T::RuntimeCall>,
139 _len: usize,
140 _self_implicit: Self::Implicit,
141 _inherited_implication: &impl parity_scale_codec::Encode,
142 _source: TransactionSource,
143 ) -> sp_runtime::traits::ValidateResult<Self::Val, T::RuntimeCall> {
144 let Some(who) = origin.as_system_origin_signer() else {
145 return Ok((ValidTransaction::default(), Val::Refund(self.weight(call)), origin));
146 };
147 let validity = match call.is_sub_type() {
148 Some(Call::retire_handle {}) => Self::validate_retire_handle(who),
149 _ => Ok(Default::default()),
150 };
151 validity.map(|v| (v, Val::Valid, origin))
152 }
153
154 fn prepare(
155 self,
156 val: Self::Val,
157 _origin: &<T as frame_system::Config>::RuntimeOrigin,
158 _call: &T::RuntimeCall,
159 _info: &DispatchInfoOf<T::RuntimeCall>,
160 _len: usize,
161 ) -> Result<Self::Pre, TransactionValidityError> {
162 match val {
163 Val::Valid => Ok(Pre::Valid),
164 Val::Refund(w) => Ok(Pre::Refund(w)),
165 }
166 }
167
168 fn post_dispatch_details(
169 pre: Self::Pre,
170 _info: &DispatchInfo,
171 _post_info: &PostDispatchInfoOf<T::RuntimeCall>,
172 _len: usize,
173 _result: &DispatchResult,
174 ) -> Result<Weight, sp_runtime::transaction_validity::TransactionValidityError> {
175 match pre {
176 Pre::Valid => Ok(Weight::zero()),
177 Pre::Refund(w) => Ok(w),
178 }
179 }
180}