frame_system/extensions/
check_mortality.rs1use crate::{pallet_prelude::BlockNumberFor, BlockHash, Config, Pallet};
19use codec::{Decode, DecodeWithMemTracking, Encode};
20use frame_support::pallet_prelude::TransactionSource;
21use scale_info::TypeInfo;
22use sp_runtime::{
23 generic::Era,
24 impl_tx_ext_default,
25 traits::{DispatchInfoOf, SaturatedConversion, TransactionExtension, ValidateResult},
26 transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
27};
28
29#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
38#[scale_info(skip_type_params(T))]
39pub struct CheckMortality<T: Config + Send + Sync>(pub Era, core::marker::PhantomData<T>);
40
41impl<T: Config + Send + Sync> CheckMortality<T> {
42 pub fn from(era: Era) -> Self {
44 Self(era, core::marker::PhantomData)
45 }
46}
47
48impl<T: Config + Send + Sync> core::fmt::Debug for CheckMortality<T> {
49 #[cfg(feature = "std")]
50 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
51 write!(f, "CheckMortality({:?})", self.0)
52 }
53
54 #[cfg(not(feature = "std"))]
55 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
56 Ok(())
57 }
58}
59
60impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckMortality<T> {
61 const IDENTIFIER: &'static str = "CheckMortality";
62 type Implicit = T::Hash;
63
64 fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
65 let current_u64 = <Pallet<T>>::block_number().saturated_into::<u64>();
66 let n = self.0.birth(current_u64).saturated_into::<BlockNumberFor<T>>();
67 if !<BlockHash<T>>::contains_key(n) {
68 Err(InvalidTransaction::AncientBirthBlock.into())
69 } else {
70 Ok(<Pallet<T>>::block_hash(n))
71 }
72 }
73 type Pre = ();
74 type Val = ();
75
76 fn weight(&self, _: &T::RuntimeCall) -> sp_weights::Weight {
77 if self.0.is_immortal() {
78 <T::ExtensionsWeightInfo as super::WeightInfo>::check_mortality_immortal_transaction()
81 .set_proof_size(0)
82 } else {
83 <T::ExtensionsWeightInfo as super::WeightInfo>::check_mortality_mortal_transaction()
84 }
85 }
86
87 fn validate(
88 &self,
89 origin: <T as Config>::RuntimeOrigin,
90 _call: &T::RuntimeCall,
91 _info: &DispatchInfoOf<T::RuntimeCall>,
92 _len: usize,
93 _self_implicit: Self::Implicit,
94 _inherited_implication: &impl Encode,
95 _source: TransactionSource,
96 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
97 let current_u64 = <Pallet<T>>::block_number().saturated_into::<u64>();
98 let valid_till = self.0.death(current_u64);
99 Ok((
100 ValidTransaction {
101 longevity: valid_till.saturating_sub(current_u64),
102 ..Default::default()
103 },
104 (),
105 origin,
106 ))
107 }
108 impl_tx_ext_default!(T::RuntimeCall; prepare);
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::mock::{new_test_ext, System, Test, CALL};
115 use frame_support::{
116 dispatch::{DispatchClass, DispatchInfo, Pays},
117 weights::Weight,
118 };
119 use sp_core::H256;
120 use sp_runtime::{
121 traits::DispatchTransaction, transaction_validity::TransactionSource::External,
122 };
123
124 #[test]
125 fn signed_ext_check_era_should_work() {
126 new_test_ext().execute_with(|| {
127 assert_eq!(
129 CheckMortality::<Test>::from(Era::mortal(4, 2)).implicit().err().unwrap(),
130 InvalidTransaction::AncientBirthBlock.into(),
131 );
132
133 System::set_block_number(13);
135 <BlockHash<Test>>::insert(12, H256::repeat_byte(1));
136 assert!(CheckMortality::<Test>::from(Era::mortal(4, 12)).implicit().is_ok());
137 })
138 }
139
140 #[test]
141 fn signed_ext_check_era_should_change_longevity() {
142 new_test_ext().execute_with(|| {
143 let normal = DispatchInfo {
144 call_weight: Weight::from_parts(100, 0),
145 extension_weight: Weight::zero(),
146 class: DispatchClass::Normal,
147 pays_fee: Pays::Yes,
148 };
149 let len = 0_usize;
150 let ext = (
151 crate::CheckWeight::<Test>::new(),
152 CheckMortality::<Test>::from(Era::mortal(16, 256)),
153 );
154 System::set_block_number(17);
155 <BlockHash<Test>>::insert(16, H256::repeat_byte(1));
156
157 assert_eq!(
158 ext.validate_only(Some(1).into(), CALL, &normal, len, External, 0)
159 .unwrap()
160 .0
161 .longevity,
162 15
163 );
164 })
165 }
166}