pallet_domains/
extensions.rs1use crate::pallet::Call as DomainsCall;
4use crate::weights::WeightInfo;
5use crate::{Config, FraudProofFor, OpaqueBundleOf, Origin, Pallet as Domains, SingletonReceiptOf};
6use frame_support::ensure;
7use frame_support::pallet_prelude::{PhantomData, TypeInfo};
8use frame_support::weights::Weight;
9use frame_system::pallet_prelude::RuntimeCallFor;
10use parity_scale_codec::{Decode, Encode};
11use scale_info::prelude::fmt;
12use sp_domains_fraud_proof::InvalidTransactionCode;
13use sp_domains_fraud_proof::weights::fraud_proof_verification_weights;
14use sp_runtime::impl_tx_ext_default;
15use sp_runtime::traits::{
16 AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Get, Implication,
17 TransactionExtension, ValidateResult,
18};
19use sp_runtime::transaction_validity::{
20 InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
21 ValidTransaction,
22};
23
24pub trait MaybeDomainsCall<Runtime>
26where
27 Runtime: Config,
28{
29 fn maybe_domains_call(&self) -> Option<&DomainsCall<Runtime>>;
30}
31
32pub trait DomainsCheck {
34 fn is_domains_enabled() -> bool;
36}
37
38#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
40pub struct DomainsExtension<Runtime>(PhantomData<Runtime>);
41
42impl<Runtime> DomainsExtension<Runtime> {
43 pub fn new() -> Self {
44 Self(PhantomData)
45 }
46}
47
48impl<Runtime> Default for DomainsExtension<Runtime> {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl<T: Config> fmt::Debug for DomainsExtension<T> {
55 #[cfg(feature = "std")]
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "DomainsExtension",)
58 }
59
60 #[cfg(not(feature = "std"))]
61 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
62 Ok(())
63 }
64}
65
66impl<Runtime> DomainsExtension<Runtime>
67where
68 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
69{
70 fn do_validate_submit_bundle(
71 opaque_bundle: &OpaqueBundleOf<Runtime>,
72 source: TransactionSource,
73 ) -> TransactionValidity {
74 let pre_dispatch = TransactionSource::InBlock == source;
75 if pre_dispatch {
76 Domains::<Runtime>::validate_submit_bundle(opaque_bundle, true)
77 .map_err(|_| InvalidTransaction::Call.into())
78 .map(|_| ValidTransaction::default())
79 } else {
80 let domain_id = opaque_bundle.domain_id();
81 let operator_id = opaque_bundle.operator_id();
82 let slot_number = opaque_bundle.slot_number();
83
84 if let Err(e) = Domains::<Runtime>::validate_submit_bundle(opaque_bundle, false) {
85 Domains::<Runtime>::log_bundle_error(&e, domain_id, operator_id);
86 return e.into();
87 }
88
89 ValidTransaction::with_tag_prefix("SubspaceSubmitBundle")
90 .priority(1)
93 .longevity(
94 Runtime::ConfirmationDepthK::get()
95 .try_into()
96 .unwrap_or_else(|_| {
97 panic!("Block number always fits in TransactionLongevity; qed")
98 }),
99 )
100 .and_provides((operator_id, slot_number))
101 .propagate(true)
102 .build()
103 }
104 }
105
106 fn do_validate_fraud_proof(
107 fraud_proof: &FraudProofFor<Runtime>,
108 source: TransactionSource,
109 ) -> TransactionValidity {
110 let pre_dispatch = TransactionSource::InBlock == source;
111 if pre_dispatch {
112 Domains::<Runtime>::validate_fraud_proof(fraud_proof)
113 .map(|_| ())
114 .map_err(|_| InvalidTransaction::Call.into())
115 .map(|_| ValidTransaction::default())
116 } else {
117 let (tag, priority) = match Domains::<Runtime>::validate_fraud_proof(fraud_proof) {
118 Err(e) => {
119 log::warn!("Bad fraud proof {fraud_proof:?}, error: {e:?}",);
120 return InvalidTransactionCode::FraudProof.into();
121 }
122 Ok(tp) => tp,
123 };
124
125 ValidTransaction::with_tag_prefix("SubspaceSubmitFraudProof")
126 .priority(priority)
127 .and_provides(tag)
128 .longevity(TransactionLongevity::MAX)
129 .propagate(true)
131 .build()
132 }
133 }
134
135 fn do_validate_singleton_receipt(
136 singleton_receipt: &SingletonReceiptOf<Runtime>,
137 source: TransactionSource,
138 ) -> TransactionValidity {
139 let pre_dispatch = TransactionSource::InBlock == source;
140 if pre_dispatch {
141 Domains::<Runtime>::validate_singleton_receipt(singleton_receipt, true)
142 .map_err(|_| InvalidTransaction::Call.into())
143 .map(|_| ValidTransaction::default())
144 } else {
145 let domain_id = singleton_receipt.domain_id();
146 let operator_id = singleton_receipt.operator_id();
147 let slot_number = singleton_receipt.slot_number();
148
149 if let Err(e) = Domains::<Runtime>::validate_singleton_receipt(singleton_receipt, false)
150 {
151 Domains::<Runtime>::log_bundle_error(&e, domain_id, operator_id);
152 return e.into();
153 }
154
155 ValidTransaction::with_tag_prefix("SubspaceSubmitReceipt")
156 .priority(1)
159 .longevity(
160 Runtime::ConfirmationDepthK::get()
161 .try_into()
162 .unwrap_or_else(|_| {
163 panic!("Block number always fits in TransactionLongevity; qed")
164 }),
165 )
166 .and_provides((operator_id, slot_number))
167 .propagate(true)
168 .build()
169 }
170 }
171}
172
173impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>> for DomainsExtension<Runtime>
174where
175 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync + DomainsCheck,
176 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
177 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone,
178 RuntimeCallFor<Runtime>: MaybeDomainsCall<Runtime>,
179{
180 const IDENTIFIER: &'static str = "DomainsExtension";
181 type Implicit = ();
182 type Val = ();
183 type Pre = ();
184
185 fn weight(&self, call: &Runtime::RuntimeCall) -> Weight {
186 let maybe_weight = match call.maybe_domains_call() {
188 Some(DomainsCall::submit_bundle { .. }) => {
189 Some(<Runtime as Config>::WeightInfo::validate_submit_bundle())
190 }
191 Some(DomainsCall::submit_fraud_proof { fraud_proof }) => Some(
192 <Runtime as Config>::WeightInfo::fraud_proof_pre_check()
193 .saturating_add(fraud_proof_verification_weights::<_, _, _, _>(fraud_proof)),
194 ),
195 Some(DomainsCall::submit_receipt { .. }) => {
196 Some(<Runtime as Config>::WeightInfo::validate_singleton_receipt())
197 }
198 _ => None,
199 };
200
201 maybe_weight
203 .and_then(|weight| weight.checked_add(&Runtime::DbWeight::get().reads(1)))
204 .unwrap_or(Runtime::DbWeight::get().reads(1))
205 }
206
207 fn validate(
208 &self,
209 origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
210 call: &RuntimeCallFor<Runtime>,
211 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
212 _len: usize,
213 _self_implicit: Self::Implicit,
214 _inherited_implication: &impl Implication,
215 source: TransactionSource,
216 ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
217 if origin.as_system_origin_signer().is_some() {
219 return Ok((ValidTransaction::default(), (), origin));
220 };
221
222 let domains_call = match call.maybe_domains_call() {
223 Some(domains_call) => domains_call,
224 None => return Ok((ValidTransaction::default(), (), origin)),
225 };
226
227 ensure!(Runtime::is_domains_enabled(), InvalidTransaction::Call);
229
230 let validity = match domains_call {
231 DomainsCall::submit_bundle { opaque_bundle } => {
232 Self::do_validate_submit_bundle(opaque_bundle, source)?
233 }
234 DomainsCall::submit_fraud_proof { fraud_proof } => {
235 Self::do_validate_fraud_proof(fraud_proof, source)?
236 }
237 DomainsCall::submit_receipt { singleton_receipt } => {
238 Self::do_validate_singleton_receipt(singleton_receipt, source)?
239 }
240 _ => return Err(InvalidTransaction::Call.into()),
241 };
242
243 Ok((validity, (), Origin::ValidatedUnsigned.into()))
244 }
245
246 impl_tx_ext_default!(RuntimeCallFor<Runtime>; prepare);
247}