pallet_domains/
extensions.rs1use crate::pallet::Call as DomainsCall;
4use crate::{Config, FraudProofFor, OpaqueBundleOf, Origin, Pallet as Domains, SingletonReceiptOf};
5use frame_support::pallet_prelude::{PhantomData, TypeInfo};
6use frame_system::pallet_prelude::RuntimeCallFor;
7use parity_scale_codec::{Decode, Encode};
8use scale_info::prelude::fmt;
9use sp_domains_fraud_proof::InvalidTransactionCode;
10use sp_runtime::impl_tx_ext_default;
11use sp_runtime::traits::{
12 AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Get, Implication,
13 TransactionExtension, ValidateResult,
14};
15use sp_runtime::transaction_validity::{
16 InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
17 ValidTransaction,
18};
19
20pub trait MaybeDomainsCall<Runtime>
22where
23 Runtime: Config,
24{
25 fn maybe_domains_call(&self) -> Option<&DomainsCall<Runtime>>;
26}
27
28#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
30pub struct DomainsExtension<Runtime>(PhantomData<Runtime>);
31
32impl<Runtime> DomainsExtension<Runtime> {
33 pub fn new() -> Self {
34 Self(PhantomData)
35 }
36}
37
38impl<Runtime> Default for DomainsExtension<Runtime> {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl<T: Config> fmt::Debug for DomainsExtension<T> {
45 #[cfg(feature = "std")]
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(f, "DomainsExtension",)
48 }
49
50 #[cfg(not(feature = "std"))]
51 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
52 Ok(())
53 }
54}
55
56impl<Runtime> DomainsExtension<Runtime>
57where
58 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
59{
60 fn do_validate_submit_bundle(
61 opaque_bundle: &OpaqueBundleOf<Runtime>,
62 source: TransactionSource,
63 ) -> TransactionValidity {
64 let pre_dispatch = TransactionSource::InBlock == source;
65 if pre_dispatch {
66 Domains::<Runtime>::validate_submit_bundle(opaque_bundle, true)
67 .map_err(|_| InvalidTransaction::Call.into())
68 .map(|_| ValidTransaction::default())
69 } else {
70 let domain_id = opaque_bundle.domain_id();
71 let operator_id = opaque_bundle.operator_id();
72 let slot_number = opaque_bundle.slot_number();
73
74 if let Err(e) = Domains::<Runtime>::validate_submit_bundle(opaque_bundle, false) {
75 Domains::<Runtime>::log_bundle_error(&e, domain_id, operator_id);
76 return e.into();
77 }
78
79 ValidTransaction::with_tag_prefix("SubspaceSubmitBundle")
80 .priority(1)
83 .longevity(
84 Runtime::ConfirmationDepthK::get()
85 .try_into()
86 .unwrap_or_else(|_| {
87 panic!("Block number always fits in TransactionLongevity; qed")
88 }),
89 )
90 .and_provides((operator_id, slot_number))
91 .propagate(true)
92 .build()
93 }
94 }
95
96 fn do_validate_fraud_proof(
97 fraud_proof: &FraudProofFor<Runtime>,
98 source: TransactionSource,
99 ) -> TransactionValidity {
100 let pre_dispatch = TransactionSource::InBlock == source;
101 if pre_dispatch {
102 Domains::<Runtime>::validate_fraud_proof(fraud_proof)
103 .map(|_| ())
104 .map_err(|_| InvalidTransaction::Call.into())
105 .map(|_| ValidTransaction::default())
106 } else {
107 let (tag, priority) = match Domains::<Runtime>::validate_fraud_proof(fraud_proof) {
108 Err(e) => {
109 log::warn!(
110 target: "runtime::domains",
111 "Bad fraud proof {fraud_proof:?}, error: {e:?}",
112 );
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 validate(
179 &self,
180 origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
181 call: &RuntimeCallFor<Runtime>,
182 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
183 _len: usize,
184 _self_implicit: Self::Implicit,
185 _inherited_implication: &impl Implication,
186 source: TransactionSource,
187 ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
188 if origin.as_system_origin_signer().is_some() {
190 return Ok((ValidTransaction::default(), (), origin));
191 };
192
193 let domains_call = match call.maybe_domains_call() {
194 Some(domains_call) => domains_call,
195 None => return Ok((ValidTransaction::default(), (), origin)),
196 };
197
198 let validity = match domains_call {
199 DomainsCall::submit_bundle { opaque_bundle } => {
200 Self::do_validate_submit_bundle(opaque_bundle, source)?
201 }
202 DomainsCall::submit_fraud_proof { fraud_proof } => {
203 Self::do_validate_fraud_proof(fraud_proof, source)?
204 }
205 DomainsCall::submit_receipt { singleton_receipt } => {
206 Self::do_validate_singleton_receipt(singleton_receipt, source)?
207 }
208 _ => return Err(InvalidTransaction::Call.into()),
209 };
210
211 Ok((validity, (), Origin::ValidatedUnsigned.into()))
212 }
213
214 impl_tx_ext_default!(RuntimeCallFor<Runtime>; prepare);
215
216 impl_tx_ext_default!(RuntimeCallFor<Runtime>; weight);
218}