1#![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 #[pallet::config]
49 pub trait Config: frame_system::Config {
50 #[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 type RuntimeCall: From<Call<Self>> + Encode;
59
60 type XcmSender: SendXcm;
61 }
62
63 #[pallet::storage]
65 pub(super) type Targets<T: Config> = StorageValue<
66 _,
67 BoundedVec<(ParaId, BoundedVec<u8, MaxPayloadSize>), MaxParachains>,
68 ValueQuery,
69 >;
70
71 #[pallet::storage]
73 pub(super) type PingCount<T: Config> = StorageValue<_, u32, ValueQuery>;
74
75 #[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 TooManyTargets,
96 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 == ¶) {
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 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 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 Self::deposit_event(Event::UnknownPong(para, seq, payload));
245 }
246 Ok(())
247 }
248 }
249}