// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Pezcumulus. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Pallet to spam the XCM/UMP. #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; use alloc::{vec, vec::Vec}; use cumulus_pallet_xcm::{ensure_sibling_para, Origin as CumulusOrigin}; use cumulus_primitives_core::ParaId; use pezframe_support::{parameter_types, BoundedVec}; use pezframe_system::Config as SystemConfig; use pezsp_runtime::traits::Saturating; use xcm::latest::prelude::*; pub use pallet::*; parameter_types! { const MaxTeyrchains: u32 = 100; const MaxPayloadSize: u32 = 1024; } #[pezframe_support::pallet] pub mod pallet { use super::*; use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; #[pallet::pallet] pub struct Pallet(_); /// The module configuration trait. #[pallet::config] pub trait Config: pezframe_system::Config { /// The overarching event type. #[allow(deprecated)] type RuntimeEvent: From> + IsType<::RuntimeEvent>; type RuntimeOrigin: From<::RuntimeOrigin> + Into::RuntimeOrigin>>; /// The overarching call type; we assume sibling chains use the same type. type RuntimeCall: From> + Encode; type XcmSender: SendXcm; } /// The target teyrchains to ping. #[pallet::storage] pub(super) type Targets = StorageValue< _, BoundedVec<(ParaId, BoundedVec), MaxTeyrchains>, 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, BlockNumberFor, OptionQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { PingSent(ParaId, u32, Vec, XcmHash, Assets), Pinged(ParaId, u32, Vec), PongSent(ParaId, u32, Vec, XcmHash, Assets), Ponged(ParaId, u32, Vec, BlockNumberFor), ErrorSendingPing(SendError, ParaId, u32, Vec), ErrorSendingPong(SendError, ParaId, u32, Vec), UnknownPong(ParaId, u32, Vec), } #[pallet::error] pub enum Error { /// Too many teyrchains have been added as a target. TooManyTargets, /// The payload provided is too large, limit is 1024 bytes. PayloadTooLarge, } #[pallet::hooks] impl Hooks> for Pallet { fn on_finalize(n: BlockNumberFor) { for (para, payload) in Targets::::get().into_iter() { let seq = PingCount::::mutate(|seq| { *seq += 1; *seq }); match send_xcm::( (Parent, Junction::Teyrchain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, call: ::RuntimeCall::from(Call::::ping { seq, payload: payload.clone().to_vec(), }) .encode() .into(), fallback_max_weight: None, }]), ) { Ok((hash, cost)) => { Pings::::insert(seq, n); Self::deposit_event(Event::PingSent( para, seq, payload.to_vec(), hash, cost, )); }, Err(e) => { Self::deposit_event(Event::ErrorSendingPing( e, para, seq, payload.to_vec(), )); }, } } } } #[pallet::call] impl Pallet { #[pallet::call_index(0)] #[pallet::weight({0})] pub fn start(origin: OriginFor, para: ParaId, payload: Vec) -> DispatchResult { ensure_root(origin)?; let payload = BoundedVec::::try_from(payload) .map_err(|_| Error::::PayloadTooLarge)?; Targets::::try_mutate(|t| { t.try_push((para, payload)).map_err(|_| Error::::TooManyTargets) })?; Ok(()) } #[pallet::call_index(1)] #[pallet::weight({0})] pub fn start_many( origin: OriginFor, para: ParaId, count: u32, payload: Vec, ) -> DispatchResult { ensure_root(origin)?; let bounded_payload = BoundedVec::::try_from(payload) .map_err(|_| Error::::PayloadTooLarge)?; for _ in 0..count { Targets::::try_mutate(|t| { t.try_push((para, bounded_payload.clone())) .map_err(|_| Error::::TooManyTargets) })?; } Ok(()) } #[pallet::call_index(2)] #[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::call_index(3)] #[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::call_index(4)] #[pallet::weight({0})] pub fn ping(origin: OriginFor, seq: u32, payload: Vec) -> DispatchResult { // Only accept pings from other chains. let para = ensure_sibling_para(::RuntimeOrigin::from(origin))?; Self::deposit_event(Event::Pinged(para, seq, payload.clone())); match send_xcm::( (Parent, Junction::Teyrchain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, call: ::RuntimeCall::from(Call::::pong { seq, payload: payload.clone(), }) .encode() .into(), fallback_max_weight: None, }]), ) { Ok((hash, cost)) => Self::deposit_event(Event::PongSent(para, seq, payload, hash, cost)), Err(e) => Self::deposit_event(Event::ErrorSendingPong(e, para, seq, payload)), } Ok(()) } #[pallet::call_index(5)] #[pallet::weight({0})] pub fn pong(origin: OriginFor, seq: u32, payload: Vec) -> DispatchResult { // Only accept pings from other chains. let para = ensure_sibling_para(::RuntimeOrigin::from(origin))?; if let Some(sent_at) = Pings::::take(seq) { Self::deposit_event(Event::Ponged( para, seq, payload, pezframe_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(()) } } }