1#[cfg(feature = "runtime-benchmarks")]
4pub mod benchmarking_from_consensus;
5#[cfg(feature = "runtime-benchmarks")]
6pub mod benchmarking_from_domains;
7pub mod weights;
8mod weights_from_consensus;
9mod weights_from_domains;
10
11use crate::extensions::weights::{FromConsensusWeightInfo, FromDomainWeightInfo};
12use crate::pallet::Call as MessengerCall;
13use crate::{
14 Call, Config, ExtensionWeightInfo, Origin, Pallet as Messenger, ValidatedRelayMessage,
15 XDM_TRANSACTION_LONGEVITY,
16};
17use core::cmp::Ordering;
18use frame_support::pallet_prelude::{PhantomData, TypeInfo, Weight};
19use frame_support::RuntimeDebugNoBound;
20use frame_system::pallet_prelude::RuntimeCallFor;
21use parity_scale_codec::{Decode, Encode};
22use scale_info::prelude::fmt;
23use sp_messenger::messages::{Message, Nonce, Proof};
24use sp_messenger::MAX_FUTURE_ALLOWED_NONCES;
25use sp_runtime::traits::{
26 AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Implication,
27 PostDispatchInfoOf, TransactionExtension, ValidateResult,
28};
29use sp_runtime::transaction_validity::{
30 InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction,
31 ValidTransactionBuilder,
32};
33use sp_runtime::DispatchResult;
34use sp_subspace_mmr::MmrProofVerifier;
35
36pub trait MaybeMessengerCall<Runtime>
38where
39 Runtime: Config,
40{
41 fn maybe_messenger_call(&self) -> Option<&MessengerCall<Runtime>>;
42}
43
44#[derive(RuntimeDebugNoBound)]
46pub enum Val<T: Config + fmt::Debug> {
47 None,
49 ValidatedRelayMessage(ValidatedRelayMessage<T>),
51}
52
53#[derive(RuntimeDebugNoBound)]
55pub enum Pre {
56 Refund(Weight),
57}
58
59#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
61pub struct MessengerExtension<Runtime>(PhantomData<Runtime>);
62
63impl<Runtime> MessengerExtension<Runtime> {
64 pub fn new() -> Self {
65 Self(PhantomData)
66 }
67}
68
69impl<Runtime> Default for MessengerExtension<Runtime> {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl<T: Config> fmt::Debug for MessengerExtension<T> {
76 #[cfg(feature = "std")]
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "MessengerExtension",)
79 }
80
81 #[cfg(not(feature = "std"))]
82 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
83 Ok(())
84 }
85}
86
87impl<Runtime> MessengerExtension<Runtime>
88where
89 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
90{
91 fn check_future_nonce_and_add_requires(
92 mut valid_tx_builder: ValidTransactionBuilder,
93 validated_relay_message: &ValidatedRelayMessage<Runtime>,
94 ) -> Result<ValidTransactionBuilder, TransactionValidityError> {
95 let Message {
96 dst_chain_id,
97 channel_id,
98 nonce: msg_nonce,
99 ..
100 } = &validated_relay_message.message;
101
102 let next_nonce = validated_relay_message.next_nonce;
103 if *msg_nonce > next_nonce {
105 let max_future_nonce = next_nonce.saturating_add(MAX_FUTURE_ALLOWED_NONCES.into());
106 if *msg_nonce > max_future_nonce {
107 return Err(InvalidTransaction::Custom(
108 crate::verification_errors::IN_FUTURE_NONCE,
109 )
110 .into());
111 }
112
113 valid_tx_builder =
114 valid_tx_builder.and_requires((dst_chain_id, channel_id, msg_nonce - Nonce::one()));
115 };
116
117 Ok(valid_tx_builder)
118 }
119
120 fn do_validate(
121 call: &MessengerCall<Runtime>,
122 ) -> Result<(ValidTransaction, ValidatedRelayMessage<Runtime>), TransactionValidityError> {
123 match call {
124 Call::relay_message { msg: xdm } => {
125 let consensus_state_root =
126 Runtime::MmrProofVerifier::verify_proof_and_extract_leaf(
127 xdm.proof.consensus_mmr_proof(),
128 )
129 .ok_or(InvalidTransaction::BadProof)?
130 .state_root();
131
132 let validated_message =
133 Messenger::<Runtime>::validate_relay_message(xdm, consensus_state_root)?;
134
135 let Message {
136 dst_chain_id,
137 channel_id,
138 nonce: msg_nonce,
139 ..
140 } = &validated_message.message;
141
142 let valid_tx_builder = Self::check_future_nonce_and_add_requires(
143 ValidTransaction::with_tag_prefix("MessengerInbox"),
144 &validated_message,
145 )?;
146
147 let validity = valid_tx_builder
148 .priority(1)
151 .longevity(XDM_TRANSACTION_LONGEVITY)
152 .and_provides((dst_chain_id, channel_id, msg_nonce))
153 .propagate(true)
154 .build()?;
155
156 Ok((validity, validated_message))
157 }
158 Call::relay_message_response { msg: xdm } => {
159 let consensus_state_root =
160 Runtime::MmrProofVerifier::verify_proof_and_extract_leaf(
161 xdm.proof.consensus_mmr_proof(),
162 )
163 .ok_or(InvalidTransaction::BadProof)?
164 .state_root();
165
166 let validated_message = Messenger::<Runtime>::validate_relay_message_response(
167 xdm,
168 consensus_state_root,
169 )?;
170
171 let Message {
172 dst_chain_id,
173 channel_id,
174 nonce: msg_nonce,
175 ..
176 } = &validated_message.message;
177
178 let valid_tx_builder = Self::check_future_nonce_and_add_requires(
179 ValidTransaction::with_tag_prefix("MessengerOutboxResponse"),
180 &validated_message,
181 )?;
182
183 let validity = valid_tx_builder
184 .priority(1)
187 .longevity(XDM_TRANSACTION_LONGEVITY)
188 .and_provides((dst_chain_id, channel_id, msg_nonce))
189 .propagate(true)
190 .build()?;
191
192 Ok((validity, validated_message))
193 }
194 _ => Err(InvalidTransaction::Call.into()),
195 }
196 }
197
198 fn do_prepare(
199 call: &MessengerCall<Runtime>,
200 val: ValidatedRelayMessage<Runtime>,
201 ) -> Result<Pre, TransactionValidityError> {
202 let ValidatedRelayMessage {
203 message,
204 should_init_channel,
205 next_nonce,
206 } = val;
207
208 if message.nonce.cmp(&next_nonce) == Ordering::Greater {
210 return Err(InvalidTransaction::Future.into());
211 }
212
213 let pre = match call {
214 Call::relay_message { msg } => {
215 Messenger::<Runtime>::pre_dispatch_relay_message(message, should_init_channel)?;
216 if should_init_channel {
217 Pre::Refund(Weight::zero())
220 } else {
221 match msg.proof {
222 Proof::Consensus { .. } => Pre::Refund(Self::refund_weight_for_consensus()),
223 Proof::Domain { .. } => Pre::Refund(Self::refund_weight_for_domains()),
224 }
225 }
226 }
227 Call::relay_message_response { .. } => {
228 Messenger::<Runtime>::pre_dispatch_relay_message_response(message)?;
229 Pre::Refund(Weight::zero())
231 }
232 _ => return Err(InvalidTransaction::Call.into()),
233 };
234
235 Ok(pre)
236 }
237
238 fn do_calculate_weight(call: &RuntimeCallFor<Runtime>) -> Weight
239 where
240 RuntimeCallFor<Runtime>: MaybeMessengerCall<Runtime>,
241 Runtime: Config,
242 {
243 let messenger_call = match call.maybe_messenger_call() {
244 Some(messenger_call) => messenger_call,
245 None => return Weight::zero(),
246 };
247
248 let (dst_chain_id, verification_weight) = match messenger_call {
249 Call::relay_message { msg } => (
250 msg.dst_chain_id,
251 match msg.proof {
252 Proof::Consensus { .. } => {
253 Runtime::ExtensionWeightInfo::from_consensus_relay_message().max(
254 Runtime::ExtensionWeightInfo::from_consensus_relay_message_channel_open(
255 ),
256 )
257 }
258 Proof::Domain { .. } => {
259 Runtime::ExtensionWeightInfo::from_domains_relay_message_channel_open()
260 .max(Runtime::ExtensionWeightInfo::from_domains_relay_message())
261 }
262 },
263 ),
264 Call::relay_message_response { msg } => (
265 msg.dst_chain_id,
266 match msg.proof {
267 Proof::Consensus { .. } => {
268 Runtime::ExtensionWeightInfo::from_consensus_relay_message_response()
269 }
270 Proof::Domain { .. } => {
271 Runtime::ExtensionWeightInfo::from_domains_relay_message_response()
272 }
273 },
274 ),
275 _ => return Weight::zero(),
276 };
277
278 let mmr_proof_weight = if dst_chain_id.is_consensus_chain() {
279 Runtime::ExtensionWeightInfo::mmr_proof_verification_on_consensus()
280 } else {
281 Runtime::ExtensionWeightInfo::mmr_proof_verification_on_domain()
282 };
283
284 mmr_proof_weight.saturating_add(verification_weight)
285 }
286
287 fn refund_weight_for_consensus() -> Weight {
288 let min = Runtime::ExtensionWeightInfo::from_consensus_relay_message_channel_open()
289 .min(Runtime::ExtensionWeightInfo::from_consensus_relay_message());
290 let max = Runtime::ExtensionWeightInfo::from_consensus_relay_message_channel_open()
291 .max(Runtime::ExtensionWeightInfo::from_consensus_relay_message());
292 max.saturating_sub(min)
293 }
294
295 fn refund_weight_for_domains() -> Weight {
296 let min = Runtime::ExtensionWeightInfo::from_domains_relay_message_channel_open()
297 .min(Runtime::ExtensionWeightInfo::from_domains_relay_message());
298 let max = Runtime::ExtensionWeightInfo::from_domains_relay_message_channel_open()
299 .max(Runtime::ExtensionWeightInfo::from_domains_relay_message());
300 max.saturating_sub(min)
301 }
302}
303
304impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>> for MessengerExtension<Runtime>
305where
306 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
307 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
308 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone,
309 RuntimeCallFor<Runtime>: MaybeMessengerCall<Runtime>,
310{
311 const IDENTIFIER: &'static str = "MessengerExtension";
312 type Implicit = ();
313 type Val = Val<Runtime>;
314 type Pre = Pre;
315
316 fn weight(&self, call: &RuntimeCallFor<Runtime>) -> Weight {
317 Self::do_calculate_weight(call)
318 }
319
320 fn validate(
321 &self,
322 origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
323 call: &RuntimeCallFor<Runtime>,
324 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
325 _len: usize,
326 _self_implicit: Self::Implicit,
327 _inherited_implication: &impl Implication,
328 _source: TransactionSource,
329 ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
330 if origin.as_system_origin_signer().is_some() {
332 return Ok((ValidTransaction::default(), Val::None, origin));
333 };
334
335 let messenger_call = match call.maybe_messenger_call() {
336 Some(messenger_call) => messenger_call,
337 None => return Ok((ValidTransaction::default(), Val::None, origin)),
338 };
339
340 let (validity, validated_relay_message) = Self::do_validate(messenger_call)?;
341 Ok((
342 validity,
343 Val::ValidatedRelayMessage(validated_relay_message),
344 Origin::ValidatedUnsigned.into(),
345 ))
346 }
347
348 fn prepare(
349 self,
350 val: Self::Val,
351 _origin: &DispatchOriginOf<RuntimeCallFor<Runtime>>,
352 call: &RuntimeCallFor<Runtime>,
353 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
354 _len: usize,
355 ) -> Result<Self::Pre, TransactionValidityError> {
356 match (call.maybe_messenger_call(), val) {
357 (Some(messenger_call), Val::ValidatedRelayMessage(validated_relay_message)) => {
359 Self::do_prepare(messenger_call, validated_relay_message)
360 }
361 (_, _) => Ok(Pre::Refund(Weight::zero())),
364 }
365 }
366
367 fn post_dispatch_details(
368 pre: Self::Pre,
369 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
370 _post_info: &PostDispatchInfoOf<RuntimeCallFor<Runtime>>,
371 _len: usize,
372 _result: &DispatchResult,
373 ) -> Result<Weight, TransactionValidityError> {
374 let Pre::Refund(weight) = pre;
375 Ok(weight)
376 }
377}
378
379#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
381pub struct MessengerTrustedMmrExtension<Runtime>(PhantomData<Runtime>);
382
383impl<Runtime> MessengerTrustedMmrExtension<Runtime> {
384 pub fn new() -> Self {
385 Self(PhantomData)
386 }
387}
388
389impl<Runtime> Default for MessengerTrustedMmrExtension<Runtime> {
390 fn default() -> Self {
391 Self::new()
392 }
393}
394
395impl<T: Config> fmt::Debug for MessengerTrustedMmrExtension<T> {
396 #[cfg(feature = "std")]
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 write!(f, "MessengerTrustedMmrExtension",)
399 }
400
401 #[cfg(not(feature = "std"))]
402 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
403 Ok(())
404 }
405}
406
407impl<Runtime> MessengerTrustedMmrExtension<Runtime>
408where
409 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
410{
411 fn do_validate(
412 call: &MessengerCall<Runtime>,
413 ) -> Result<(ValidTransaction, ValidatedRelayMessage<Runtime>), TransactionValidityError> {
414 match call {
415 Call::relay_message { msg: xdm } => {
416 let consensus_state_root =
417 Runtime::MmrProofVerifier::extract_leaf_without_verifying(
418 xdm.proof.consensus_mmr_proof(),
419 )
420 .ok_or(InvalidTransaction::BadProof)?
421 .state_root();
422
423 let validated_relay_message =
424 Messenger::<Runtime>::validate_relay_message(xdm, consensus_state_root)?;
425
426 Ok((ValidTransaction::default(), validated_relay_message))
427 }
428 Call::relay_message_response { msg: xdm } => {
429 let consensus_state_root =
430 Runtime::MmrProofVerifier::extract_leaf_without_verifying(
431 xdm.proof.consensus_mmr_proof(),
432 )
433 .ok_or(InvalidTransaction::BadProof)?
434 .state_root();
435
436 let validated_relay_message =
437 Messenger::<Runtime>::validate_relay_message_response(
438 xdm,
439 consensus_state_root,
440 )?;
441
442 Ok((ValidTransaction::default(), validated_relay_message))
443 }
444 _ => Err(InvalidTransaction::Call.into()),
445 }
446 }
447}
448
449impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>>
450 for MessengerTrustedMmrExtension<Runtime>
451where
452 Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
453 <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
454 AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone,
455 RuntimeCallFor<Runtime>: MaybeMessengerCall<Runtime>,
456{
457 const IDENTIFIER: &'static str = "MessengerTrustedMmrExtension";
458 type Implicit = ();
459 type Val = Val<Runtime>;
460 type Pre = Pre;
461
462 fn weight(&self, call: &RuntimeCallFor<Runtime>) -> Weight {
463 MessengerExtension::<Runtime>::do_calculate_weight(call)
464 }
465
466 fn validate(
467 &self,
468 origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
469 call: &RuntimeCallFor<Runtime>,
470 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
471 _len: usize,
472 _self_implicit: Self::Implicit,
473 _inherited_implication: &impl Implication,
474 _source: TransactionSource,
475 ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
476 if origin.as_system_origin_signer().is_some() {
478 return Ok((ValidTransaction::default(), Val::None, origin));
479 };
480
481 let messenger_call = match call.maybe_messenger_call() {
482 Some(messenger_call) => messenger_call,
483 None => return Ok((ValidTransaction::default(), Val::None, origin)),
484 };
485
486 let (validity, validated_relay_message) = Self::do_validate(messenger_call)?;
487 Ok((
488 validity,
489 Val::ValidatedRelayMessage(validated_relay_message),
490 Origin::ValidatedUnsigned.into(),
491 ))
492 }
493
494 fn prepare(
495 self,
496 val: Self::Val,
497 _origin: &DispatchOriginOf<RuntimeCallFor<Runtime>>,
498 call: &RuntimeCallFor<Runtime>,
499 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
500 _len: usize,
501 ) -> Result<Self::Pre, TransactionValidityError> {
502 match (call.maybe_messenger_call(), val) {
503 (Some(messenger_call), Val::ValidatedRelayMessage(validated_relay_message)) => {
505 MessengerExtension::<Runtime>::do_prepare(messenger_call, validated_relay_message)
506 }
507 (_, _) => Ok(Pre::Refund(Weight::zero())),
509 }
510 }
511
512 fn post_dispatch_details(
513 pre: Self::Pre,
514 _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
515 _post_info: &PostDispatchInfoOf<RuntimeCallFor<Runtime>>,
516 _len: usize,
517 _result: &DispatchResult,
518 ) -> Result<Weight, TransactionValidityError> {
519 let Pre::Refund(weight) = pre;
520 Ok(weight)
521 }
522}