pallet_subspace/
extensions.rs1#[cfg(feature = "runtime-benchmarks")]
4pub mod benchmarking;
5pub mod weights;
6
7use crate::extensions::weights::WeightInfo;
8use crate::pallet::Call as SubspaceCall;
9use crate::{Config, Origin, Pallet as Subspace};
10use frame_support::pallet_prelude::{PhantomData, TypeInfo, Weight};
11use frame_support::RuntimeDebugNoBound;
12use frame_system::pallet_prelude::{BlockNumberFor, RuntimeCallFor};
13use parity_scale_codec::{Decode, Encode};
14use scale_info::prelude::fmt;
15use sp_consensus_subspace::SignedVote;
16use sp_runtime::traits::{
17 AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Implication,
18 PostDispatchInfoOf, TransactionExtension, ValidateResult,
19};
20use sp_runtime::transaction_validity::{
21 InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction,
22};
23use sp_runtime::DispatchResult;
24
25pub trait MaybeSubspaceCall<Runtime>
27where
28 Runtime: Config,
29{
30 fn maybe_subspace_call(&self) -> Option<&SubspaceCall<Runtime>>;
31}
32
33#[derive(RuntimeDebugNoBound)]
35pub enum ExtensionWeightData {
36 Validated(Weight),
38 Skipped,
40}
41
42#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
44pub struct SubspaceExtension<Runtime>(PhantomData<Runtime>);
45
46impl<Runtime> SubspaceExtension<Runtime> {
47 pub fn new() -> Self {
48 Self(PhantomData)
49 }
50}
51
52impl<Runtime> Default for SubspaceExtension<Runtime> {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl<T: Config> fmt::Debug for SubspaceExtension<T> {
59 #[cfg(feature = "std")]
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "SubspaceExtension",)
62 }
63
64 #[cfg(not(feature = "std"))]
65 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
66 Ok(())
67 }
68}
69
70impl<Runtime> SubspaceExtension<Runtime>
71where
72 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
73{
74 fn do_check_vote(
75 signed_vote: &SignedVote<BlockNumberFor<Runtime>, Runtime::Hash, Runtime::AccountId>,
76 source: TransactionSource,
77 ) -> Result<(ValidTransaction, Weight), TransactionValidityError> {
78 let pre_dispatch = source == TransactionSource::InBlock;
79 if pre_dispatch {
80 Subspace::<Runtime>::pre_dispatch_vote(signed_vote)
81 .map(|weight| (ValidTransaction::default(), weight))
82 } else {
83 Subspace::<Runtime>::validate_vote(signed_vote)
84 }
85 }
86
87 fn max_weight() -> Weight {
88 <Runtime as Config>::ExtensionWeightInfo::vote()
89 .max(<Runtime as Config>::ExtensionWeightInfo::vote_with_equivocation())
90 }
91}
92
93impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>> for SubspaceExtension<Runtime>
94where
95 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
96 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
97 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone,
98 RuntimeCallFor<Runtime>: MaybeSubspaceCall<Runtime>,
99{
100 const IDENTIFIER: &'static str = "SubspaceExtension";
101 type Implicit = ();
102 type Val = ExtensionWeightData;
103 type Pre = ExtensionWeightData;
104
105 fn weight(&self, call: &RuntimeCallFor<Runtime>) -> Weight {
106 match call.maybe_subspace_call() {
107 Some(SubspaceCall::vote { .. }) => Self::max_weight(),
108 _ => Weight::zero(),
109 }
110 }
111
112 fn validate(
113 &self,
114 origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
115 call: &RuntimeCallFor<Runtime>,
116 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
117 _len: usize,
118 _self_implicit: Self::Implicit,
119 _inherited_implication: &impl Implication,
120 source: TransactionSource,
121 ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
122 if origin.as_system_origin_signer().is_some() {
124 return Ok((
125 ValidTransaction::default(),
126 ExtensionWeightData::Skipped,
127 origin,
128 ));
129 };
130
131 let subspace_call = match call.maybe_subspace_call() {
132 Some(subspace_call) => subspace_call,
133 None => {
134 return Ok((
135 ValidTransaction::default(),
136 ExtensionWeightData::Skipped,
137 origin,
138 ))
139 }
140 };
141
142 let (validity, weight_used) = match subspace_call {
143 SubspaceCall::vote { signed_vote } => Self::do_check_vote(signed_vote, source)?,
144 _ => return Err(InvalidTransaction::Call.into()),
145 };
146
147 Ok((
148 validity,
149 ExtensionWeightData::Validated(weight_used),
150 Origin::ValidatedUnsigned.into(),
151 ))
152 }
153
154 fn prepare(
155 self,
156 val: Self::Val,
157 _origin: &DispatchOriginOf<RuntimeCallFor<Runtime>>,
158 _call: &RuntimeCallFor<Runtime>,
159 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
160 _len: usize,
161 ) -> Result<Self::Pre, TransactionValidityError> {
162 Ok(val)
163 }
164
165 fn post_dispatch_details(
166 pre: Self::Pre,
167 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
168 _post_info: &PostDispatchInfoOf<RuntimeCallFor<Runtime>>,
169 _len: usize,
170 _result: &DispatchResult,
171 ) -> Result<Weight, TransactionValidityError> {
172 match pre {
173 ExtensionWeightData::Validated(used_weight) => {
175 Ok(Self::max_weight().saturating_sub(used_weight))
176 }
177 ExtensionWeightData::Skipped => Ok(Weight::zero()),
179 }
180 }
181}