referrerpolicy=no-referrer-when-downgrade

cumulus_ping/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Pallet to spam the XCM/UMP.
18
19#![cfg_attr(not(feature = "std"), no_std)]
20
21extern crate alloc;
22
23use alloc::{vec, vec::Vec};
24use cumulus_pallet_xcm::{ensure_sibling_para, Origin as CumulusOrigin};
25use cumulus_primitives_core::ParaId;
26use frame_support::{parameter_types, BoundedVec};
27use frame_system::Config as SystemConfig;
28use sp_runtime::traits::Saturating;
29use xcm::latest::prelude::*;
30
31pub use pallet::*;
32
33parameter_types! {
34	const MaxParachains: u32 = 100;
35	const MaxPayloadSize: u32 = 1024;
36}
37
38#[frame_support::pallet]
39pub mod pallet {
40	use super::*;
41	use frame_support::pallet_prelude::*;
42	use frame_system::pallet_prelude::*;
43
44	#[pallet::pallet]
45	pub struct Pallet<T>(_);
46
47	/// The module configuration trait.
48	#[pallet::config]
49	pub trait Config: frame_system::Config {
50		/// The overarching event type.
51		#[allow(deprecated)]
52		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
53
54		type RuntimeOrigin: From<<Self as SystemConfig>::RuntimeOrigin>
55			+ Into<Result<CumulusOrigin, <Self as Config>::RuntimeOrigin>>;
56
57		/// The overarching call type; we assume sibling chains use the same type.
58		type RuntimeCall: From<Call<Self>> + Encode;
59
60		type XcmSender: SendXcm;
61	}
62
63	/// The target parachains to ping.
64	#[pallet::storage]
65	pub(super) type Targets<T: Config> = StorageValue<
66		_,
67		BoundedVec<(ParaId, BoundedVec<u8, MaxPayloadSize>), MaxParachains>,
68		ValueQuery,
69	>;
70
71	/// The total number of pings sent.
72	#[pallet::storage]
73	pub(super) type PingCount<T: Config> = StorageValue<_, u32, ValueQuery>;
74
75	/// The sent pings.
76	#[pallet::storage]
77	pub(super) type Pings<T: Config> =
78		StorageMap<_, Blake2_128Concat, u32, BlockNumberFor<T>, OptionQuery>;
79
80	#[pallet::event]
81	#[pallet::generate_deposit(pub(super) fn deposit_event)]
82	pub enum Event<T: Config> {
83		PingSent(ParaId, u32, Vec<u8>, XcmHash, Assets),
84		Pinged(ParaId, u32, Vec<u8>),
85		PongSent(ParaId, u32, Vec<u8>, XcmHash, Assets),
86		Ponged(ParaId, u32, Vec<u8>, BlockNumberFor<T>),
87		ErrorSendingPing(SendError, ParaId, u32, Vec<u8>),
88		ErrorSendingPong(SendError, ParaId, u32, Vec<u8>),
89		UnknownPong(ParaId, u32, Vec<u8>),
90	}
91
92	#[pallet::error]
93	pub enum Error<T> {
94		/// Too many parachains have been added as a target.
95		TooManyTargets,
96		/// The payload provided is too large, limit is 1024 bytes.
97		PayloadTooLarge,
98	}
99
100	#[pallet::hooks]
101	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
102		fn on_finalize(n: BlockNumberFor<T>) {
103			for (para, payload) in Targets::<T>::get().into_iter() {
104				let seq = PingCount::<T>::mutate(|seq| {
105					*seq += 1;
106					*seq
107				});
108				match send_xcm::<T::XcmSender>(
109					(Parent, Junction::Parachain(para.into())).into(),
110					Xcm(vec![Transact {
111						origin_kind: OriginKind::Native,
112						call: <T as Config>::RuntimeCall::from(Call::<T>::ping {
113							seq,
114							payload: payload.clone().to_vec(),
115						})
116						.encode()
117						.into(),
118						fallback_max_weight: None,
119					}]),
120				) {
121					Ok((hash, cost)) => {
122						Pings::<T>::insert(seq, n);
123						Self::deposit_event(Event::PingSent(
124							para,
125							seq,
126							payload.to_vec(),
127							hash,
128							cost,
129						));
130					},
131					Err(e) => {
132						Self::deposit_event(Event::ErrorSendingPing(
133							e,
134							para,
135							seq,
136							payload.to_vec(),
137						));
138					},
139				}
140			}
141		}
142	}
143
144	#[pallet::call]
145	impl<T: Config> Pallet<T> {
146		#[pallet::call_index(0)]
147		#[pallet::weight({0})]
148		pub fn start(origin: OriginFor<T>, para: ParaId, payload: Vec<u8>) -> DispatchResult {
149			ensure_root(origin)?;
150			let payload = BoundedVec::<u8, MaxPayloadSize>::try_from(payload)
151				.map_err(|_| Error::<T>::PayloadTooLarge)?;
152			Targets::<T>::try_mutate(|t| {
153				t.try_push((para, payload)).map_err(|_| Error::<T>::TooManyTargets)
154			})?;
155			Ok(())
156		}
157
158		#[pallet::call_index(1)]
159		#[pallet::weight({0})]
160		pub fn start_many(
161			origin: OriginFor<T>,
162			para: ParaId,
163			count: u32,
164			payload: Vec<u8>,
165		) -> DispatchResult {
166			ensure_root(origin)?;
167			let bounded_payload = BoundedVec::<u8, MaxPayloadSize>::try_from(payload)
168				.map_err(|_| Error::<T>::PayloadTooLarge)?;
169			for _ in 0..count {
170				Targets::<T>::try_mutate(|t| {
171					t.try_push((para, bounded_payload.clone()))
172						.map_err(|_| Error::<T>::TooManyTargets)
173				})?;
174			}
175			Ok(())
176		}
177
178		#[pallet::call_index(2)]
179		#[pallet::weight({0})]
180		pub fn stop(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
181			ensure_root(origin)?;
182			Targets::<T>::mutate(|t| {
183				if let Some(p) = t.iter().position(|(p, _)| p == &para) {
184					t.swap_remove(p);
185				}
186			});
187			Ok(())
188		}
189
190		#[pallet::call_index(3)]
191		#[pallet::weight({0})]
192		pub fn stop_all(origin: OriginFor<T>, maybe_para: Option<ParaId>) -> DispatchResult {
193			ensure_root(origin)?;
194			if let Some(para) = maybe_para {
195				Targets::<T>::mutate(|t| t.retain(|&(x, _)| x != para));
196			} else {
197				Targets::<T>::kill();
198			}
199			Ok(())
200		}
201
202		#[pallet::call_index(4)]
203		#[pallet::weight({0})]
204		pub fn ping(origin: OriginFor<T>, seq: u32, payload: Vec<u8>) -> DispatchResult {
205			// Only accept pings from other chains.
206			let para = ensure_sibling_para(<T as Config>::RuntimeOrigin::from(origin))?;
207
208			Self::deposit_event(Event::Pinged(para, seq, payload.clone()));
209			match send_xcm::<T::XcmSender>(
210				(Parent, Junction::Parachain(para.into())).into(),
211				Xcm(vec![Transact {
212					origin_kind: OriginKind::Native,
213					call: <T as Config>::RuntimeCall::from(Call::<T>::pong {
214						seq,
215						payload: payload.clone(),
216					})
217					.encode()
218					.into(),
219					fallback_max_weight: None,
220				}]),
221			) {
222				Ok((hash, cost)) =>
223					Self::deposit_event(Event::PongSent(para, seq, payload, hash, cost)),
224				Err(e) => Self::deposit_event(Event::ErrorSendingPong(e, para, seq, payload)),
225			}
226			Ok(())
227		}
228
229		#[pallet::call_index(5)]
230		#[pallet::weight({0})]
231		pub fn pong(origin: OriginFor<T>, seq: u32, payload: Vec<u8>) -> DispatchResult {
232			// Only accept pings from other chains.
233			let para = ensure_sibling_para(<T as Config>::RuntimeOrigin::from(origin))?;
234
235			if let Some(sent_at) = Pings::<T>::take(seq) {
236				Self::deposit_event(Event::Ponged(
237					para,
238					seq,
239					payload,
240					frame_system::Pallet::<T>::block_number().saturating_sub(sent_at),
241				));
242			} else {
243				// Pong received for a ping we apparently didn't send?!
244				Self::deposit_event(Event::UnknownPong(para, seq, payload));
245			}
246			Ok(())
247		}
248	}
249}