frame_system/extensions/
check_mortality.rs1use crate::{pallet_prelude::BlockNumberFor, BlockHash, Config, Pallet};
19use codec::{Decode, Encode};
20use scale_info::TypeInfo;
21use sp_runtime::{
22 generic::Era,
23 traits::{DispatchInfoOf, SaturatedConversion, SignedExtension},
24 transaction_validity::{
25 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
26 },
27};
28
29#[derive(Encode, Decode, 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> SignedExtension for CheckMortality<T> {
61 type AccountId = T::AccountId;
62 type Call = T::RuntimeCall;
63 type AdditionalSigned = T::Hash;
64 type Pre = ();
65 const IDENTIFIER: &'static str = "CheckMortality";
66
67 fn validate(
68 &self,
69 _who: &Self::AccountId,
70 _call: &Self::Call,
71 _info: &DispatchInfoOf<Self::Call>,
72 _len: usize,
73 ) -> TransactionValidity {
74 let current_u64 = <Pallet<T>>::block_number().saturated_into::<u64>();
75 let valid_till = self.0.death(current_u64);
76 Ok(ValidTransaction {
77 longevity: valid_till.saturating_sub(current_u64),
78 ..Default::default()
79 })
80 }
81
82 fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
83 let current_u64 = <Pallet<T>>::block_number().saturated_into::<u64>();
84 let n = self.0.birth(current_u64).saturated_into::<BlockNumberFor<T>>();
85 if !<BlockHash<T>>::contains_key(n) {
86 Err(InvalidTransaction::AncientBirthBlock.into())
87 } else {
88 Ok(<Pallet<T>>::block_hash(n))
89 }
90 }
91
92 fn pre_dispatch(
93 self,
94 who: &Self::AccountId,
95 call: &Self::Call,
96 info: &DispatchInfoOf<Self::Call>,
97 len: usize,
98 ) -> Result<Self::Pre, TransactionValidityError> {
99 self.validate(who, call, info, len).map(|_| ())
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::mock::{new_test_ext, System, Test, CALL};
107 use frame_support::{
108 dispatch::{DispatchClass, DispatchInfo, Pays},
109 weights::Weight,
110 };
111 use sp_core::H256;
112
113 #[test]
114 fn signed_ext_check_era_should_work() {
115 new_test_ext().execute_with(|| {
116 assert_eq!(
118 CheckMortality::<Test>::from(Era::mortal(4, 2))
119 .additional_signed()
120 .err()
121 .unwrap(),
122 InvalidTransaction::AncientBirthBlock.into(),
123 );
124
125 System::set_block_number(13);
127 <BlockHash<Test>>::insert(12, H256::repeat_byte(1));
128 assert!(CheckMortality::<Test>::from(Era::mortal(4, 12)).additional_signed().is_ok());
129 })
130 }
131
132 #[test]
133 fn signed_ext_check_era_should_change_longevity() {
134 new_test_ext().execute_with(|| {
135 let normal = DispatchInfo {
136 weight: Weight::from_parts(100, 0),
137 class: DispatchClass::Normal,
138 pays_fee: Pays::Yes,
139 };
140 let len = 0_usize;
141 let ext = (
142 crate::CheckWeight::<Test>::new(),
143 CheckMortality::<Test>::from(Era::mortal(16, 256)),
144 );
145 System::set_block_number(17);
146 <BlockHash<Test>>::insert(16, H256::repeat_byte(1));
147
148 assert_eq!(ext.validate(&1, CALL, &normal, len).unwrap().longevity, 15);
149 })
150 }
151}