// This file is part of Substrate. // Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. // 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. //! Parallel tasks example //! //! This example pallet parallelizes validation of the enlisted participants //! (see `enlist_participants` dispatch). #![cfg_attr(not(feature = "std"), no_std)] use frame_system::ensure_signed; use frame_support::{ dispatch::DispatchResult, decl_module, decl_storage, decl_event, }; use sp_runtime::RuntimeDebug; use codec::{Encode, Decode}; use sp_std::vec::Vec; #[cfg(test)] mod tests; pub trait Config: frame_system::Config { /// The overarching event type. type Event: From + Into<::Event>; /// The overarching dispatch call type. type Call: From>; } decl_storage! { trait Store for Module as ExampleOffchainWorker { /// A vector of current participants /// /// To enlist someone to participate, signed payload should be /// sent to `enlist`. Participants get(fn participants): Vec>; /// Current event id to enlist participants to. CurrentEventId get(fn get_current_event_id): Vec; } } decl_event!( /// Events generated by the module. pub enum Event { /// When new event is drafted. NewEventDrafted(Vec), } ); /// Request to enlist participant. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct EnlistedParticipant { pub account: Vec, pub signature: Vec, } impl EnlistedParticipant { fn verify(&self, event_id: &[u8]) -> bool { use sp_core::Public; use std::convert::TryFrom; use sp_runtime::traits::Verify; match sp_core::sr25519::Signature::try_from(&self.signature[..]) { Ok(signature) => { let public = sp_core::sr25519::Public::from_slice(self.account.as_ref()); signature.verify(event_id, &public) } _ => false } } } decl_module! { /// A public part of the pallet. pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; /// Get the new event running. #[weight = 0] pub fn run_event(origin, id: Vec) -> DispatchResult { let _ = ensure_signed(origin)?; Participants::kill(); CurrentEventId::mutate(move |event_id| *event_id = id); Ok(()) } /// Submit list of participants to the current event. /// /// The example utilizes parallel execution by checking half of the /// signatures in spawned task. #[weight = 0] pub fn enlist_participants(origin, participants: Vec) -> DispatchResult { let _ = ensure_signed(origin)?; if validate_participants_parallel(&CurrentEventId::get(), &participants[..]) { for participant in participants { Participants::append(participant.account); } } Ok(()) } } } fn validate_participants_parallel(event_id: &[u8], participants: &[EnlistedParticipant]) -> bool { fn spawn_verify(data: Vec) -> Vec { let stream = &mut &data[..]; let event_id = Vec::::decode(stream).expect("Failed to decode"); let participants = Vec::::decode(stream).expect("Failed to decode"); for participant in participants { if !participant.verify(&event_id) { return false.encode() } } true.encode() } let mut async_payload = Vec::new(); event_id.encode_to(&mut async_payload); participants[..participants.len() / 2].encode_to(&mut async_payload); let handle = sp_tasks::spawn(spawn_verify, async_payload); let mut result = true; for participant in &participants[participants.len()/2+1..] { if !participant.verify(event_id) { result = false; break; } } bool::decode(&mut &handle.join()[..]).expect("Failed to decode result") && result }