pallet_subspace/
extensions.rs

1//! Extensions for unsigned general extrinsics
2
3#[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
25/// Trait to convert Runtime call to possible Subspace call.
26pub trait MaybeSubspaceCall<Runtime>
27where
28    Runtime: Config,
29{
30    fn maybe_subspace_call(&self) -> Option<&SubspaceCall<Runtime>>;
31}
32
33/// Weight info used by this extension
34#[derive(RuntimeDebugNoBound)]
35pub enum ExtensionWeightData {
36    /// Represents the validated call's used weight
37    Validated(Weight),
38    /// Skipped validation
39    Skipped,
40}
41
42/// Extensions for pallet-subspace unsigned extrinsics.
43#[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        // we only care about unsigned calls
123        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            // return the unused weight for a validated call.
174            ExtensionWeightData::Validated(used_weight) => {
175                Ok(Self::max_weight().saturating_sub(used_weight))
176            }
177            // return no weight since this call is not validated and took no weight.
178            ExtensionWeightData::Skipped => Ok(Weight::zero()),
179        }
180    }
181}