// Copyright 2020-2021 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Cumulus is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . //! Pallet to spam the XCM/UMP. #![cfg_attr(not(feature = "std"), no_std)] use sp_std::prelude::*; use sp_runtime::traits::Saturating; use frame_system::Config as SystemConfig; use cumulus_primitives_core::ParaId; use cumulus_pallet_xcm::{Origin as CumulusOrigin, ensure_sibling_para}; use xcm::latest::{Xcm, Error as XcmError, SendXcm, OriginKind, MultiLocation, Junction}; pub use pallet::*; #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// The module configuration trait. #[pallet::config] pub trait Config: frame_system::Config { /// The overarching event type. type Event: From> + IsType<::Event>; type Origin: From<::Origin> + Into::Origin>>; /// The overarching call type; we assume sibling chains use the same type. type Call: From> + Encode; type XcmSender: SendXcm; } /// The target parachains to ping. #[pallet::storage] pub(super) type Targets = StorageValue< _, Vec<(ParaId, Vec)>, ValueQuery, >; /// The total number of pings sent. #[pallet::storage] pub(super) type PingCount = StorageValue< _, u32, ValueQuery, >; /// The sent pings. #[pallet::storage] pub(super) type Pings = StorageMap< _, Blake2_128Concat, u32, T::BlockNumber, OptionQuery, >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] #[pallet::metadata(T::BlockNumber = "BlockNumber")] pub enum Event { PingSent(ParaId, u32, Vec), Pinged(ParaId, u32, Vec), PongSent(ParaId, u32, Vec), Ponged(ParaId, u32, Vec, T::BlockNumber), ErrorSendingPing(XcmError, ParaId, u32, Vec), ErrorSendingPong(XcmError, ParaId, u32, Vec), UnknownPong(ParaId, u32, Vec), } #[pallet::error] pub enum Error {} #[pallet::hooks] impl Hooks> for Pallet { fn on_finalize( n: T::BlockNumber, ) { for (para, payload) in Targets::::get().into_iter() { let seq = PingCount::::mutate(|seq| { *seq += 1; *seq }); match T::XcmSender::send_xcm( MultiLocation::X2(Junction::Parent, Junction::Parachain(para.into())), Xcm::Transact { origin_type: OriginKind::Native, require_weight_at_most: 1_000, call: ::Call::from(Call::::ping(seq, payload.clone())).encode().into(), }, ) { Ok(()) => { Pings::::insert(seq, n); Self::deposit_event(Event::PingSent(para, seq, payload)); }, Err(e) => { Self::deposit_event(Event::ErrorSendingPing(e, para, seq, payload)); } } } } } #[pallet::call] impl Pallet { #[pallet::weight(0)] pub fn start(origin: OriginFor, para: ParaId, payload: Vec) -> DispatchResult { ensure_root(origin)?; Targets::::mutate(|t| t.push((para, payload))); Ok(()) } #[pallet::weight(0)] pub fn start_many(origin: OriginFor, para: ParaId, count: u32, payload: Vec) -> DispatchResult { ensure_root(origin)?; for _ in 0..count { Targets::::mutate(|t| t.push((para, payload.clone()))); } Ok(()) } #[pallet::weight(0)] pub fn stop(origin: OriginFor, para: ParaId) -> DispatchResult { ensure_root(origin)?; Targets::::mutate(|t| if let Some(p) = t.iter().position(|(p, _)| p == ¶) { t.swap_remove(p); }); Ok(()) } #[pallet::weight(0)] pub fn stop_all(origin: OriginFor, maybe_para: Option) -> DispatchResult { ensure_root(origin)?; if let Some(para) = maybe_para { Targets::::mutate(|t| t.retain(|&(x, _)| x != para)); } else { Targets::::kill(); } Ok(()) } #[pallet::weight(0)] pub fn ping(origin: OriginFor, seq: u32, payload: Vec) -> DispatchResult { // Only accept pings from other chains. let para = ensure_sibling_para(::Origin::from(origin))?; Self::deposit_event(Event::Pinged(para, seq, payload.clone())); match T::XcmSender::send_xcm( MultiLocation::X2(Junction::Parent, Junction::Parachain(para.into())), Xcm::Transact { origin_type: OriginKind::Native, require_weight_at_most: 1_000, call: ::Call::from(Call::::pong(seq, payload.clone())).encode().into(), }, ) { Ok(()) => Self::deposit_event(Event::PongSent(para, seq, payload)), Err(e) => Self::deposit_event(Event::ErrorSendingPong(e, para, seq, payload)), } Ok(()) } #[pallet::weight(0)] pub fn pong(origin: OriginFor, seq: u32, payload: Vec) -> DispatchResult { // Only accept pings from other chains. let para = ensure_sibling_para(::Origin::from(origin))?; if let Some(sent_at) = Pings::::take(seq) { Self::deposit_event(Event::Ponged(para, seq, payload, frame_system::Pallet::::block_number().saturating_sub(sent_at))); } else { // Pong received for a ping we apparently didn't send?! Self::deposit_event(Event::UnknownPong(para, seq, payload)); } Ok(()) } } }