pallet_messenger/
extensions.rs

1//! Extensions for unsigned general extrinsics
2
3use crate::pallet::Call as MessengerCall;
4use crate::{
5    Call, Config, Origin, Pallet as Messenger, ValidatedRelayMessage, XDM_TRANSACTION_LONGEVITY,
6};
7use core::cmp::Ordering;
8use frame_support::pallet_prelude::{PhantomData, TypeInfo};
9use frame_support::RuntimeDebugNoBound;
10use frame_system::pallet_prelude::RuntimeCallFor;
11use parity_scale_codec::{Decode, Encode};
12use scale_info::prelude::fmt;
13use sp_messenger::messages::{Message, Nonce};
14use sp_messenger::MAX_FUTURE_ALLOWED_NONCES;
15use sp_runtime::impl_tx_ext_default;
16use sp_runtime::traits::{
17    AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Implication,
18    TransactionExtension, ValidateResult,
19};
20use sp_runtime::transaction_validity::{
21    InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction,
22    ValidTransactionBuilder,
23};
24use sp_subspace_mmr::MmrProofVerifier;
25
26/// Trait to convert Runtime call to possible Messenger call.
27pub trait MaybeMessengerCall<Runtime>
28where
29    Runtime: Config,
30{
31    fn maybe_messenger_call(&self) -> Option<&MessengerCall<Runtime>>;
32}
33
34/// Data passed from validate to prepare.
35#[derive(RuntimeDebugNoBound)]
36pub enum Val<T: Config + fmt::Debug> {
37    /// No validation data
38    None,
39    /// Validated data
40    ValidatedRelayMessage(ValidatedRelayMessage<T>),
41}
42
43/// Extensions for pallet-messenger unsigned extrinsics.
44#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
45pub struct MessengerExtension<Runtime>(PhantomData<Runtime>);
46
47impl<Runtime> MessengerExtension<Runtime> {
48    pub fn new() -> Self {
49        Self(PhantomData)
50    }
51}
52
53impl<Runtime> Default for MessengerExtension<Runtime> {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl<T: Config> fmt::Debug for MessengerExtension<T> {
60    #[cfg(feature = "std")]
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        write!(f, "MessengerExtension",)
63    }
64
65    #[cfg(not(feature = "std"))]
66    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
67        Ok(())
68    }
69}
70
71impl<Runtime> MessengerExtension<Runtime>
72where
73    Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
74{
75    fn check_future_nonce_and_add_requires(
76        mut valid_tx_builder: ValidTransactionBuilder,
77        validated_relay_message: &ValidatedRelayMessage<Runtime>,
78    ) -> Result<ValidTransactionBuilder, TransactionValidityError> {
79        let Message {
80            dst_chain_id,
81            channel_id,
82            nonce: msg_nonce,
83            ..
84        } = &validated_relay_message.message;
85
86        let next_nonce = validated_relay_message.next_nonce;
87        // Only add the requires tag if the msg nonce is in future
88        if *msg_nonce > next_nonce {
89            let max_future_nonce = next_nonce.saturating_add(MAX_FUTURE_ALLOWED_NONCES.into());
90            if *msg_nonce > max_future_nonce {
91                return Err(InvalidTransaction::Custom(
92                    crate::verification_errors::IN_FUTURE_NONCE,
93                )
94                .into());
95            }
96
97            valid_tx_builder =
98                valid_tx_builder.and_requires((dst_chain_id, channel_id, msg_nonce - Nonce::one()));
99        };
100
101        Ok(valid_tx_builder)
102    }
103
104    fn do_validate(
105        call: &MessengerCall<Runtime>,
106    ) -> Result<(ValidTransaction, ValidatedRelayMessage<Runtime>), TransactionValidityError> {
107        match call {
108            Call::relay_message { msg: xdm } => {
109                let consensus_state_root =
110                    Runtime::MmrProofVerifier::verify_proof_and_extract_leaf(
111                        xdm.proof.consensus_mmr_proof(),
112                    )
113                    .ok_or(InvalidTransaction::BadProof)?
114                    .state_root();
115
116                let validated_message =
117                    Messenger::<Runtime>::validate_relay_message(xdm, consensus_state_root)?;
118
119                let Message {
120                    dst_chain_id,
121                    channel_id,
122                    nonce: msg_nonce,
123                    ..
124                } = &validated_message.message;
125
126                let valid_tx_builder = Self::check_future_nonce_and_add_requires(
127                    ValidTransaction::with_tag_prefix("MessengerInbox"),
128                    &validated_message,
129                )?;
130
131                let validity = valid_tx_builder
132                    // XDM have a bit higher priority than normal extrinsic but must less than
133                    // fraud proof
134                    .priority(1)
135                    .longevity(XDM_TRANSACTION_LONGEVITY)
136                    .and_provides((dst_chain_id, channel_id, msg_nonce))
137                    .propagate(true)
138                    .build()?;
139
140                Ok((validity, validated_message))
141            }
142            Call::relay_message_response { msg: xdm } => {
143                let consensus_state_root =
144                    Runtime::MmrProofVerifier::verify_proof_and_extract_leaf(
145                        xdm.proof.consensus_mmr_proof(),
146                    )
147                    .ok_or(InvalidTransaction::BadProof)?
148                    .state_root();
149
150                let validated_message = Messenger::<Runtime>::validate_relay_message_response(
151                    xdm,
152                    consensus_state_root,
153                )?;
154
155                let Message {
156                    dst_chain_id,
157                    channel_id,
158                    nonce: msg_nonce,
159                    ..
160                } = &validated_message.message;
161
162                let valid_tx_builder = Self::check_future_nonce_and_add_requires(
163                    ValidTransaction::with_tag_prefix("MessengerOutboxResponse"),
164                    &validated_message,
165                )?;
166
167                let validity = valid_tx_builder
168                    // XDM have a bit higher priority than normal extrinsic but must less than
169                    // fraud proof
170                    .priority(1)
171                    .longevity(XDM_TRANSACTION_LONGEVITY)
172                    .and_provides((dst_chain_id, channel_id, msg_nonce))
173                    .propagate(true)
174                    .build()?;
175
176                Ok((validity, validated_message))
177            }
178            _ => Err(InvalidTransaction::Call.into()),
179        }
180    }
181
182    fn do_prepare(
183        call: &MessengerCall<Runtime>,
184        val: ValidatedRelayMessage<Runtime>,
185    ) -> Result<(), TransactionValidityError> {
186        let ValidatedRelayMessage {
187            message,
188            should_init_channel,
189            next_nonce,
190        } = val;
191
192        // Reject in future message
193        if message.nonce.cmp(&next_nonce) == Ordering::Greater {
194            return Err(InvalidTransaction::Future.into());
195        }
196
197        match call {
198            Call::relay_message { .. } => {
199                Messenger::<Runtime>::pre_dispatch_relay_message(message, should_init_channel)
200            }
201            Call::relay_message_response { .. } => {
202                Messenger::<Runtime>::pre_dispatch_relay_message_response(message)
203            }
204            _ => Err(InvalidTransaction::Call.into()),
205        }
206    }
207}
208
209impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>> for MessengerExtension<Runtime>
210where
211    Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
212    <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
213        AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone,
214    RuntimeCallFor<Runtime>: MaybeMessengerCall<Runtime>,
215{
216    const IDENTIFIER: &'static str = "MessengerExtension";
217    type Implicit = ();
218    type Val = Val<Runtime>;
219    type Pre = ();
220
221    fn validate(
222        &self,
223        origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
224        call: &RuntimeCallFor<Runtime>,
225        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
226        _len: usize,
227        _self_implicit: Self::Implicit,
228        _inherited_implication: &impl Implication,
229        _source: TransactionSource,
230    ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
231        // we only care about unsigned calls
232        if origin.as_system_origin_signer().is_some() {
233            return Ok((ValidTransaction::default(), Val::None, origin));
234        };
235
236        let messenger_call = match call.maybe_messenger_call() {
237            Some(messenger_call) => messenger_call,
238            None => return Ok((ValidTransaction::default(), Val::None, origin)),
239        };
240
241        let (validity, validated_relay_message) = Self::do_validate(messenger_call)?;
242        Ok((
243            validity,
244            Val::ValidatedRelayMessage(validated_relay_message),
245            Origin::ValidatedUnsigned.into(),
246        ))
247    }
248
249    fn prepare(
250        self,
251        val: Self::Val,
252        _origin: &DispatchOriginOf<RuntimeCallFor<Runtime>>,
253        call: &RuntimeCallFor<Runtime>,
254        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
255        _len: usize,
256    ) -> Result<Self::Pre, TransactionValidityError> {
257        match (call.maybe_messenger_call(), val) {
258            // prepare if this is a messenger call and has been validated
259            (Some(messenger_call), Val::ValidatedRelayMessage(validated_relay_message)) => {
260                Self::do_prepare(messenger_call, validated_relay_message)
261            }
262            // return Ok for the rest of the call types
263            (_, _) => Ok(()),
264        }
265    }
266
267    // TODO: need benchmarking for this extension.
268    impl_tx_ext_default!(RuntimeCallFor<Runtime>; weight);
269}
270
271/// Extensions for pallet-messenger unsigned extrinsics with trusted MMR verification.
272#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
273pub struct MessengerTrustedMmrExtension<Runtime>(PhantomData<Runtime>);
274
275impl<Runtime> MessengerTrustedMmrExtension<Runtime> {
276    pub fn new() -> Self {
277        Self(PhantomData)
278    }
279}
280
281impl<Runtime> Default for MessengerTrustedMmrExtension<Runtime> {
282    fn default() -> Self {
283        Self::new()
284    }
285}
286
287impl<T: Config> fmt::Debug for MessengerTrustedMmrExtension<T> {
288    #[cfg(feature = "std")]
289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290        write!(f, "MessengerTrustedMmrExtension",)
291    }
292
293    #[cfg(not(feature = "std"))]
294    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
295        Ok(())
296    }
297}
298
299impl<Runtime> MessengerTrustedMmrExtension<Runtime>
300where
301    Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
302{
303    fn do_validate(
304        call: &MessengerCall<Runtime>,
305    ) -> Result<(ValidTransaction, ValidatedRelayMessage<Runtime>), TransactionValidityError> {
306        match call {
307            Call::relay_message { msg: xdm } => {
308                let consensus_state_root =
309                    Runtime::MmrProofVerifier::extract_leaf_without_verifying(
310                        xdm.proof.consensus_mmr_proof(),
311                    )
312                    .ok_or(InvalidTransaction::BadProof)?
313                    .state_root();
314
315                let validated_relay_message =
316                    Messenger::<Runtime>::validate_relay_message(xdm, consensus_state_root)?;
317
318                Ok((ValidTransaction::default(), validated_relay_message))
319            }
320            Call::relay_message_response { msg: xdm } => {
321                let consensus_state_root =
322                    Runtime::MmrProofVerifier::extract_leaf_without_verifying(
323                        xdm.proof.consensus_mmr_proof(),
324                    )
325                    .ok_or(InvalidTransaction::BadProof)?
326                    .state_root();
327
328                let validated_relay_message =
329                    Messenger::<Runtime>::validate_relay_message_response(
330                        xdm,
331                        consensus_state_root,
332                    )?;
333
334                Ok((ValidTransaction::default(), validated_relay_message))
335            }
336            _ => Err(InvalidTransaction::Call.into()),
337        }
338    }
339}
340
341impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>>
342    for MessengerTrustedMmrExtension<Runtime>
343where
344    Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
345    <RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
346        AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + From<Origin> + Clone,
347    RuntimeCallFor<Runtime>: MaybeMessengerCall<Runtime>,
348{
349    const IDENTIFIER: &'static str = "MessengerTrustedMmrExtension";
350    type Implicit = ();
351    type Val = Val<Runtime>;
352    type Pre = ();
353
354    // TODO: need benchmarking for this extension.
355    impl_tx_ext_default!(RuntimeCallFor<Runtime>; weight);
356
357    fn validate(
358        &self,
359        origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
360        call: &RuntimeCallFor<Runtime>,
361        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
362        _len: usize,
363        _self_implicit: Self::Implicit,
364        _inherited_implication: &impl Implication,
365        _source: TransactionSource,
366    ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
367        // we only care about unsigned calls
368        if origin.as_system_origin_signer().is_some() {
369            return Ok((ValidTransaction::default(), Val::None, origin));
370        };
371
372        let messenger_call = match call.maybe_messenger_call() {
373            Some(messenger_call) => messenger_call,
374            None => return Ok((ValidTransaction::default(), Val::None, origin)),
375        };
376
377        let (validity, validated_relay_message) = Self::do_validate(messenger_call)?;
378        Ok((
379            validity,
380            Val::ValidatedRelayMessage(validated_relay_message),
381            Origin::ValidatedUnsigned.into(),
382        ))
383    }
384
385    fn prepare(
386        self,
387        val: Self::Val,
388        _origin: &DispatchOriginOf<RuntimeCallFor<Runtime>>,
389        call: &RuntimeCallFor<Runtime>,
390        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
391        _len: usize,
392    ) -> Result<Self::Pre, TransactionValidityError> {
393        match (call.maybe_messenger_call(), val) {
394            // prepare if this is a messenger call and has been validated
395            (Some(messenger_call), Val::ValidatedRelayMessage(validated_relay_message)) => {
396                MessengerExtension::<Runtime>::do_prepare(messenger_call, validated_relay_message)
397            }
398            // return Ok for the rest of the call types
399            (_, _) => Ok(()),
400        }
401    }
402}