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