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