referrerpolicy=no-referrer-when-downgrade

frame_system/
offchain.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Module helpers for off-chain calls.
19//!
20//! ## Overview
21//!
22//! This module provides transaction related helpers to:
23//! - Submit a raw unsigned transaction
24//! - Submit an unsigned transaction with a signed payload
25//! - Submit a signed transaction.
26//!
27//! ## Usage
28//!
29//! Please refer to [`example-offchain-worker`](../../pallet_example_offchain_worker/index.html) for
30//! a concrete example usage of this crate.
31//!
32//! ### Submit a raw unsigned transaction
33//!
34//! To submit a raw unsigned transaction, [`SubmitTransaction`](./struct.SubmitTransaction.html)
35//! can be used.
36//!
37//! ### Signing transactions
38//!
39//! To be able to use signing, the following trait should be implemented:
40//!
41//! - [`AppCrypto`](./trait.AppCrypto.html): where an application-specific key is defined and can be
42//!   used by this module's helpers for signing.
43//! - [`CreateSignedTransaction`](./trait.CreateSignedTransaction.html): where the manner in which
44//!   the transaction is constructed is defined.
45//!
46//! #### Submit an unsigned transaction with a signed payload
47//!
48//! Initially, a payload instance that implements the `SignedPayload` trait should be defined.
49//! See [`PricePayload`](../../pallet_example_offchain_worker/struct.PricePayload.html)
50//!
51//! The payload type that is defined defined can then be signed and submitted onchain.
52//!
53//! #### Submit a signed transaction
54//!
55//! [`Signer`](./struct.Signer.html) can be used to sign/verify payloads
56
57#![warn(missing_docs)]
58
59use alloc::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec};
60use codec::Encode;
61use scale_info::TypeInfo;
62use sp_runtime::{
63	app_crypto::RuntimeAppPublic,
64	traits::{ExtrinsicLike, IdentifyAccount, One},
65	RuntimeDebug,
66};
67
68/// Marker struct used to flag using all supported keys to sign a payload.
69pub struct ForAll {}
70/// Marker struct used to flag using any of the supported keys to sign a payload.
71pub struct ForAny {}
72
73/// Provides the ability to directly submit signed and unsigned
74/// transaction onchain.
75///
76/// For submitting unsigned transactions, `submit_unsigned_transaction`
77/// utility function can be used. However, this struct is used by `Signer`
78/// to submit a signed transactions providing the signature along with the call.
79pub struct SubmitTransaction<T: CreateTransactionBase<RuntimeCall>, RuntimeCall> {
80	_phantom: core::marker::PhantomData<(T, RuntimeCall)>,
81}
82
83impl<T, LocalCall> SubmitTransaction<T, LocalCall>
84where
85	T: CreateTransactionBase<LocalCall>,
86{
87	/// A convenience method to submit an extrinsic onchain.
88	pub fn submit_transaction(xt: T::Extrinsic) -> Result<(), ()> {
89		sp_io::offchain::submit_transaction(xt.encode())
90	}
91}
92
93/// Provides an implementation for signing transaction payloads.
94///
95/// Keys used for signing are defined when instantiating the signer object.
96/// Signing can be done using:
97///
98/// - All supported keys in the keystore
99/// - Any of the supported keys in the keystore
100/// - An intersection of in-keystore keys and the list of provided keys
101///
102/// The signer is then able to:
103/// - Submit a unsigned transaction with a signed payload
104/// - Submit a signed transaction
105#[derive(RuntimeDebug)]
106pub struct Signer<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>, X = ForAny> {
107	accounts: Option<Vec<T::Public>>,
108	_phantom: core::marker::PhantomData<(X, C)>,
109}
110
111impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>, X> Default for Signer<T, C, X> {
112	fn default() -> Self {
113		Self { accounts: Default::default(), _phantom: Default::default() }
114	}
115}
116
117impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>, X> Signer<T, C, X> {
118	/// Use all available keys for signing.
119	pub fn all_accounts() -> Signer<T, C, ForAll> {
120		Default::default()
121	}
122
123	/// Use any of the available keys for signing.
124	pub fn any_account() -> Signer<T, C, ForAny> {
125		Default::default()
126	}
127
128	/// Use provided `accounts` for signing.
129	///
130	/// Note that not all keys will be necessarily used. The provided
131	/// vector of accounts will be intersected with the supported keys
132	/// in the keystore and the resulting list will be used for signing.
133	pub fn with_filter(mut self, accounts: Vec<T::Public>) -> Self {
134		self.accounts = Some(accounts);
135		self
136	}
137
138	/// Check if there are any keys that could be used for signing.
139	pub fn can_sign(&self) -> bool {
140		self.accounts_from_keys().count() > 0
141	}
142
143	/// Return a vector of the intersection between
144	/// all available accounts and the provided accounts
145	/// in `with_filter`. If no accounts are provided,
146	/// use all accounts by default.
147	pub fn accounts_from_keys<'a>(&'a self) -> Box<dyn Iterator<Item = Account<T>> + 'a> {
148		let keystore_accounts = Self::keystore_accounts();
149		match self.accounts {
150			None => Box::new(keystore_accounts),
151			Some(ref keys) => {
152				let keystore_lookup: BTreeSet<<T as SigningTypes>::Public> =
153					keystore_accounts.map(|account| account.public).collect();
154
155				Box::new(
156					keys.iter()
157						.enumerate()
158						.map(|(index, key)| {
159							let account_id = key.clone().into_account();
160							Account::new(index, account_id, key.clone())
161						})
162						.filter(move |account| keystore_lookup.contains(&account.public)),
163				)
164			},
165		}
166	}
167
168	/// Return all available accounts in keystore.
169	pub fn keystore_accounts() -> impl Iterator<Item = Account<T>> {
170		C::RuntimeAppPublic::all().into_iter().enumerate().map(|(index, key)| {
171			let generic_public = C::GenericPublic::from(key);
172			let public: T::Public = generic_public.into();
173			let account_id = public.clone().into_account();
174			Account::new(index, account_id, public)
175		})
176	}
177}
178
179impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> Signer<T, C, ForAll> {
180	fn for_all<F, R>(&self, f: F) -> Vec<(Account<T>, R)>
181	where
182		F: Fn(&Account<T>) -> Option<R>,
183	{
184		let accounts = self.accounts_from_keys();
185		accounts
186			.into_iter()
187			.filter_map(|account| f(&account).map(|res| (account, res)))
188			.collect()
189	}
190}
191
192impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> Signer<T, C, ForAny> {
193	fn for_any<F, R>(&self, f: F) -> Option<(Account<T>, R)>
194	where
195		F: Fn(&Account<T>) -> Option<R>,
196	{
197		let accounts = self.accounts_from_keys();
198		for account in accounts.into_iter() {
199			let res = f(&account);
200			if let Some(res) = res {
201				return Some((account, res))
202			}
203		}
204		None
205	}
206}
207
208impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> SignMessage<T>
209	for Signer<T, C, ForAll>
210{
211	type SignatureData = Vec<(Account<T>, T::Signature)>;
212
213	fn sign_message(&self, message: &[u8]) -> Self::SignatureData {
214		self.for_all(|account| C::sign(message, account.public.clone()))
215	}
216
217	fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
218	where
219		F: Fn(&Account<T>) -> TPayload,
220		TPayload: SignedPayload<T>,
221	{
222		self.for_all(|account| f(account).sign::<C>())
223	}
224}
225
226impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> SignMessage<T>
227	for Signer<T, C, ForAny>
228{
229	type SignatureData = Option<(Account<T>, T::Signature)>;
230
231	fn sign_message(&self, message: &[u8]) -> Self::SignatureData {
232		self.for_any(|account| C::sign(message, account.public.clone()))
233	}
234
235	fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
236	where
237		F: Fn(&Account<T>) -> TPayload,
238		TPayload: SignedPayload<T>,
239	{
240		self.for_any(|account| f(account).sign::<C>())
241	}
242}
243
244impl<
245		T: CreateSignedTransaction<LocalCall> + SigningTypes,
246		C: AppCrypto<T::Public, T::Signature>,
247		LocalCall,
248	> SendSignedTransaction<T, C, LocalCall> for Signer<T, C, ForAny>
249{
250	type Result = Option<(Account<T>, Result<(), ()>)>;
251
252	fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result {
253		self.for_any(|account| {
254			let call = f(account);
255			self.send_single_signed_transaction(account, call)
256		})
257	}
258}
259
260impl<
261		T: SigningTypes + CreateSignedTransaction<LocalCall>,
262		C: AppCrypto<T::Public, T::Signature>,
263		LocalCall,
264	> SendSignedTransaction<T, C, LocalCall> for Signer<T, C, ForAll>
265{
266	type Result = Vec<(Account<T>, Result<(), ()>)>;
267
268	fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result {
269		self.for_all(|account| {
270			let call = f(account);
271			self.send_single_signed_transaction(account, call)
272		})
273	}
274}
275
276impl<T: SigningTypes + CreateBare<LocalCall>, C: AppCrypto<T::Public, T::Signature>, LocalCall>
277	SendUnsignedTransaction<T, LocalCall> for Signer<T, C, ForAny>
278{
279	type Result = Option<(Account<T>, Result<(), ()>)>;
280
281	fn send_unsigned_transaction<TPayload, F>(
282		&self,
283		f: F,
284		f2: impl Fn(TPayload, T::Signature) -> LocalCall,
285	) -> Self::Result
286	where
287		F: Fn(&Account<T>) -> TPayload,
288		TPayload: SignedPayload<T>,
289	{
290		self.for_any(|account| {
291			let payload = f(account);
292			let signature = payload.sign::<C>()?;
293			let call = f2(payload, signature);
294			self.submit_unsigned_transaction(call)
295		})
296	}
297}
298
299impl<T: SigningTypes + CreateBare<LocalCall>, C: AppCrypto<T::Public, T::Signature>, LocalCall>
300	SendUnsignedTransaction<T, LocalCall> for Signer<T, C, ForAll>
301{
302	type Result = Vec<(Account<T>, Result<(), ()>)>;
303
304	fn send_unsigned_transaction<TPayload, F>(
305		&self,
306		f: F,
307		f2: impl Fn(TPayload, T::Signature) -> LocalCall,
308	) -> Self::Result
309	where
310		F: Fn(&Account<T>) -> TPayload,
311		TPayload: SignedPayload<T>,
312	{
313		self.for_all(|account| {
314			let payload = f(account);
315			let signature = payload.sign::<C>()?;
316			let call = f2(payload, signature);
317			self.submit_unsigned_transaction(call)
318		})
319	}
320}
321
322/// Details of an account for which a private key is contained in the keystore.
323#[derive(RuntimeDebug, PartialEq)]
324pub struct Account<T: SigningTypes> {
325	/// Index on the provided list of accounts or list of all accounts.
326	pub index: usize,
327	/// Runtime-specific `AccountId`.
328	pub id: T::AccountId,
329	/// A runtime-specific `Public` key for that key pair.
330	pub public: T::Public,
331}
332
333impl<T: SigningTypes> Account<T> {
334	/// Create a new Account instance
335	pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self {
336		Self { index, id, public }
337	}
338}
339
340impl<T: SigningTypes> Clone for Account<T>
341where
342	T::AccountId: Clone,
343	T::Public: Clone,
344{
345	fn clone(&self) -> Self {
346		Self { index: self.index, id: self.id.clone(), public: self.public.clone() }
347	}
348}
349
350/// A type binding runtime-level `Public/Signature` pair with crypto wrapped by `RuntimeAppPublic`.
351///
352/// Implementations of this trait should specify the app-specific public/signature types.
353/// This is merely a wrapper around an existing `RuntimeAppPublic` type, but with
354/// extra non-application-specific crypto type that is being wrapped (e.g. `sr25519`, `ed25519`).
355/// This is needed to later on convert into runtime-specific `Public` key, which might support
356/// multiple different crypto.
357/// The point of this trait is to be able to easily convert between `RuntimeAppPublic`, the wrapped
358/// (generic = non application-specific) crypto types and the `Public` type required by the runtime.
359///
360/// Example (pseudo-)implementation:
361/// ```ignore
362/// // im-online specific crypto
363/// type RuntimeAppPublic = ImOnline(sr25519::Public);
364///
365/// // wrapped "raw" crypto
366/// type GenericPublic = sr25519::Public;
367/// type GenericSignature = sr25519::Signature;
368///
369/// // runtime-specific public key
370/// type Public = MultiSigner: From<sr25519::Public>;
371/// type Signature = MultiSignature: From<sr25519::Signature>;
372/// ```
373// TODO [#5662] Potentially use `IsWrappedBy` types, or find some other way to make it easy to
374// obtain unwrapped crypto (and wrap it back).
375pub trait AppCrypto<Public, Signature> {
376	/// A application-specific crypto.
377	type RuntimeAppPublic: RuntimeAppPublic;
378
379	/// A raw crypto public key wrapped by `RuntimeAppPublic`.
380	type GenericPublic: From<Self::RuntimeAppPublic>
381		+ Into<Self::RuntimeAppPublic>
382		+ TryFrom<Public>
383		+ Into<Public>;
384
385	/// A matching raw crypto `Signature` type.
386	type GenericSignature: From<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>
387		+ Into<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>
388		+ TryFrom<Signature>
389		+ Into<Signature>;
390
391	/// Sign payload with the private key to maps to the provided public key.
392	fn sign(payload: &[u8], public: Public) -> Option<Signature> {
393		let p: Self::GenericPublic = public.try_into().ok()?;
394		let x = Into::<Self::RuntimeAppPublic>::into(p);
395		x.sign(&payload)
396			.map(|x| {
397				let sig: Self::GenericSignature = x.into();
398				sig
399			})
400			.map(Into::into)
401	}
402
403	/// Verify signature against the provided public key.
404	fn verify(payload: &[u8], public: Public, signature: Signature) -> bool {
405		let p: Self::GenericPublic = match public.try_into() {
406			Ok(a) => a,
407			_ => return false,
408		};
409		let x = Into::<Self::RuntimeAppPublic>::into(p);
410		let signature: Self::GenericSignature = match signature.try_into() {
411			Ok(a) => a,
412			_ => return false,
413		};
414		let signature =
415			Into::<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>::into(signature);
416
417		x.verify(&payload, &signature)
418	}
419}
420
421/// A wrapper around the types which are used for signing.
422///
423/// This trait adds extra bounds to `Public` and `Signature` types of the runtime
424/// that are necessary to use these types for signing.
425// TODO [#5663] Could this be just `T::Signature as traits::Verify>::Signer`?
426// Seems that this may cause issues with bounds resolution.
427pub trait SigningTypes: crate::Config {
428	/// A public key that is capable of identifying `AccountId`s.
429	///
430	/// Usually that's either a raw crypto public key (e.g. `sr25519::Public`) or
431	/// an aggregate type for multiple crypto public keys, like `MultiSigner`.
432	type Public: Clone
433		+ PartialEq
434		+ IdentifyAccount<AccountId = Self::AccountId>
435		+ core::fmt::Debug
436		+ codec::Codec
437		+ Ord
438		+ scale_info::TypeInfo;
439
440	/// A matching `Signature` type.
441	type Signature: Clone + PartialEq + core::fmt::Debug + codec::Codec + scale_info::TypeInfo;
442}
443
444/// Common interface for the `CreateTransaction` trait family to unify the `Call` type.
445pub trait CreateTransactionBase<LocalCall> {
446	/// The extrinsic.
447	type Extrinsic: ExtrinsicLike + Encode;
448
449	/// The runtime's call type.
450	///
451	/// This has additional bound to be able to be created from pallet-local `Call` types.
452	type RuntimeCall: From<LocalCall> + Encode;
453}
454
455/// Interface for creating a transaction.
456pub trait CreateTransaction<LocalCall>: CreateTransactionBase<LocalCall> {
457	/// The extension.
458	type Extension: TypeInfo;
459
460	/// Create a transaction using the call and the desired transaction extension.
461	fn create_transaction(
462		call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall,
463		extension: Self::Extension,
464	) -> Self::Extrinsic;
465}
466
467/// Interface for creating an old-school signed transaction.
468pub trait CreateSignedTransaction<LocalCall>:
469	CreateTransactionBase<LocalCall> + SigningTypes
470{
471	/// Attempt to create signed extrinsic data that encodes call from given account.
472	///
473	/// Runtime implementation is free to construct the payload to sign and the signature
474	/// in any way it wants.
475	/// Returns `None` if signed extrinsic could not be created (either because signing failed
476	/// or because of any other runtime-specific reason).
477	fn create_signed_transaction<C: AppCrypto<Self::Public, Self::Signature>>(
478		call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall,
479		public: Self::Public,
480		account: Self::AccountId,
481		nonce: Self::Nonce,
482	) -> Option<Self::Extrinsic>;
483}
484
485/// Interface for creating an inherent; ⚠️  **Deprecated use [`CreateBare`]**.
486///
487/// This is a deprecated type alias for [`CreateBare`].
488///
489/// Doc for [`CreateBare`]:
490#[deprecated(note = "Use `CreateBare` instead")]
491#[doc(inline)]
492pub use CreateBare as CreateInherent;
493
494/// Interface for creating a bare extrinsic.
495///
496/// Bare extrinsic are used for inherent extrinsic and unsigned transaction.
497pub trait CreateBare<LocalCall>: CreateTransactionBase<LocalCall> {
498	/// Create a bare extrinsic.
499	///
500	/// Bare extrinsic are used for inherent extrinsic and unsigned transaction.
501	fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic;
502
503	/// Create an inherent.
504	#[deprecated(note = "Use `create_bare` instead")]
505	fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic {
506		Self::create_bare(call)
507	}
508}
509
510/// A message signer.
511pub trait SignMessage<T: SigningTypes> {
512	/// A signature data.
513	///
514	/// May contain account used for signing and the `Signature` itself.
515	type SignatureData;
516
517	/// Sign a message.
518	///
519	/// Implementation of this method should return
520	/// a result containing the signature.
521	fn sign_message(&self, message: &[u8]) -> Self::SignatureData;
522
523	/// Construct and sign given payload.
524	///
525	/// This method expects `f` to return a `SignedPayload`
526	/// object which is then used for signing.
527	fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
528	where
529		F: Fn(&Account<T>) -> TPayload,
530		TPayload: SignedPayload<T>;
531}
532
533/// Interface for creating a transaction for a call that will be authorized.
534///
535/// Authorized calls are calls that has some specific validation logic execute in the transaction
536/// extension: [`crate::AuthorizeCall`].
537/// The authorization logic is defined on the call with the attribute:
538/// [`frame_support::pallet_macros::authorize`].
539///
540/// This trait allows the runtime to define the extension to be used when creating an authorized
541/// transaction. It can be used in the offchain worker to create a transaction from a call.
542pub trait CreateAuthorizedTransaction<LocalCall>: CreateTransaction<LocalCall> {
543	/// Create the transaction extension to be used alongside an authorized call.
544	///
545	/// For more information about authorized call see [`frame_support::pallet_prelude::authorize`].
546	fn create_extension() -> Self::Extension;
547
548	/// Create a new transaction for an authorized call.
549	///
550	/// For more information about authorized call see [`frame_support::pallet_prelude::authorize`].
551	fn create_authorized_transaction(call: Self::RuntimeCall) -> Self::Extrinsic {
552		Self::create_transaction(call, Self::create_extension())
553	}
554}
555
556/// Submit a signed transaction to the transaction pool.
557pub trait SendSignedTransaction<
558	T: CreateSignedTransaction<LocalCall>,
559	C: AppCrypto<T::Public, T::Signature>,
560	LocalCall,
561>
562{
563	/// A submission result.
564	///
565	/// This should contain an indication of success and the account that was used for signing.
566	type Result;
567
568	/// Submit a signed transaction to the local pool.
569	///
570	/// Given `f` closure will be called for every requested account and expects a `Call` object
571	/// to be returned.
572	/// The call is then wrapped into a transaction (see `#CreateSignedTransaction`), signed and
573	/// submitted to the pool.
574	fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result;
575
576	/// Wraps the call into transaction, signs using given account and submits to the pool.
577	fn send_single_signed_transaction(
578		&self,
579		account: &Account<T>,
580		call: LocalCall,
581	) -> Option<Result<(), ()>> {
582		let mut account_data = crate::Account::<T>::get(&account.id);
583		log::debug!(
584			target: "runtime::offchain",
585			"Creating signed transaction from account: {:?} (nonce: {:?})",
586			account.id,
587			account_data.nonce,
588		);
589		let transaction = T::create_signed_transaction::<C>(
590			call.into(),
591			account.public.clone(),
592			account.id.clone(),
593			account_data.nonce,
594		)?;
595
596		let res = SubmitTransaction::<T, LocalCall>::submit_transaction(transaction);
597
598		if res.is_ok() {
599			// increment the nonce. This is fine, since the code should always
600			// be running in off-chain context, so we NEVER persists data.
601			account_data.nonce += One::one();
602			crate::Account::<T>::insert(&account.id, account_data);
603		}
604
605		Some(res)
606	}
607}
608
609/// Submit an unsigned transaction onchain with a signed payload
610pub trait SendUnsignedTransaction<T: SigningTypes + CreateBare<LocalCall>, LocalCall> {
611	/// A submission result.
612	///
613	/// Should contain the submission result and the account(s) that signed the payload.
614	type Result;
615
616	/// Send an unsigned transaction with a signed payload.
617	///
618	/// This method takes `f` and `f2` where:
619	/// - `f` is called for every account and is expected to return a `SignedPayload` object.
620	/// - `f2` is then called with the `SignedPayload` returned by `f` and the signature and is
621	/// expected to return a `Call` object to be embedded into transaction.
622	fn send_unsigned_transaction<TPayload, F>(
623		&self,
624		f: F,
625		f2: impl Fn(TPayload, T::Signature) -> LocalCall,
626	) -> Self::Result
627	where
628		F: Fn(&Account<T>) -> TPayload,
629		TPayload: SignedPayload<T>;
630
631	/// Submits an unsigned call to the transaction pool.
632	fn submit_unsigned_transaction(&self, call: LocalCall) -> Option<Result<(), ()>> {
633		let xt = T::create_bare(call.into());
634		Some(SubmitTransaction::<T, LocalCall>::submit_transaction(xt))
635	}
636}
637
638/// Utility trait to be implemented on payloads that can be signed.
639pub trait SignedPayload<T: SigningTypes>: Encode {
640	/// Return a public key that is expected to have a matching key in the keystore,
641	/// which should be used to sign the payload.
642	fn public(&self) -> T::Public;
643
644	/// Sign the payload using the implementor's provided public key.
645	///
646	/// Returns `Some(signature)` if public key is supported.
647	fn sign<C: AppCrypto<T::Public, T::Signature>>(&self) -> Option<T::Signature> {
648		self.using_encoded(|payload| C::sign(payload, self.public()))
649	}
650
651	/// Verify signature against payload.
652	///
653	/// Returns a bool indicating whether the signature is valid or not.
654	fn verify<C: AppCrypto<T::Public, T::Signature>>(&self, signature: T::Signature) -> bool {
655		self.using_encoded(|payload| C::verify(payload, self.public(), signature))
656	}
657}
658
659#[cfg(test)]
660mod tests {
661	use super::*;
662	use crate::mock::{RuntimeCall, Test as TestRuntime, CALL};
663	use codec::Decode;
664	use sp_core::offchain::{testing, TransactionPoolExt};
665	use sp_runtime::testing::{TestSignature, TestXt, UintAuthorityId};
666
667	impl SigningTypes for TestRuntime {
668		type Public = UintAuthorityId;
669		type Signature = TestSignature;
670	}
671
672	type Extrinsic = TestXt<RuntimeCall, ()>;
673
674	impl CreateTransactionBase<RuntimeCall> for TestRuntime {
675		type Extrinsic = Extrinsic;
676		type RuntimeCall = RuntimeCall;
677	}
678
679	impl CreateBare<RuntimeCall> for TestRuntime {
680		fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic {
681			Extrinsic::new_bare(call)
682		}
683	}
684
685	#[derive(codec::Encode, codec::Decode)]
686	struct SimplePayload {
687		pub public: UintAuthorityId,
688		pub data: Vec<u8>,
689	}
690
691	impl SignedPayload<TestRuntime> for SimplePayload {
692		fn public(&self) -> UintAuthorityId {
693			self.public.clone()
694		}
695	}
696
697	struct DummyAppCrypto;
698	// Bind together the `SigningTypes` with app-crypto and the wrapper types.
699	// here the implementation is pretty dummy, because we use the same type for
700	// both application-specific crypto and the runtime crypto, but in real-life
701	// runtimes it's going to use different types everywhere.
702	impl AppCrypto<UintAuthorityId, TestSignature> for DummyAppCrypto {
703		type RuntimeAppPublic = UintAuthorityId;
704		type GenericPublic = UintAuthorityId;
705		type GenericSignature = TestSignature;
706	}
707
708	fn assert_account(next: Option<(Account<TestRuntime>, Result<(), ()>)>, index: usize, id: u64) {
709		assert_eq!(next, Some((Account { index, id, public: id.into() }, Ok(()))));
710	}
711
712	#[test]
713	fn should_send_unsigned_with_signed_payload_with_all_accounts() {
714		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
715
716		let mut t = sp_io::TestExternalities::default();
717		t.register_extension(TransactionPoolExt::new(pool));
718
719		// given
720		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
721
722		t.execute_with(|| {
723			// when
724			let result = Signer::<TestRuntime, DummyAppCrypto>::all_accounts()
725				.send_unsigned_transaction(
726					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
727					|_payload, _signature| CALL.clone(),
728				);
729
730			// then
731			let mut res = result.into_iter();
732			assert_account(res.next(), 0, 0xf0);
733			assert_account(res.next(), 1, 0xf1);
734			assert_account(res.next(), 2, 0xf2);
735			assert_eq!(res.next(), None);
736
737			// check the transaction pool content:
738			let tx1 = pool_state.write().transactions.pop().unwrap();
739			let _tx2 = pool_state.write().transactions.pop().unwrap();
740			let _tx3 = pool_state.write().transactions.pop().unwrap();
741			assert!(pool_state.read().transactions.is_empty());
742			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
743			assert!(tx1.is_inherent());
744		});
745	}
746
747	#[test]
748	fn should_send_unsigned_with_signed_payload_with_any_account() {
749		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
750
751		let mut t = sp_io::TestExternalities::default();
752		t.register_extension(TransactionPoolExt::new(pool));
753
754		// given
755		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
756
757		t.execute_with(|| {
758			// when
759			let result = Signer::<TestRuntime, DummyAppCrypto>::any_account()
760				.send_unsigned_transaction(
761					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
762					|_payload, _signature| CALL.clone(),
763				);
764
765			// then
766			let mut res = result.into_iter();
767			assert_account(res.next(), 0, 0xf0);
768			assert_eq!(res.next(), None);
769
770			// check the transaction pool content:
771			let tx1 = pool_state.write().transactions.pop().unwrap();
772			assert!(pool_state.read().transactions.is_empty());
773			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
774			assert!(tx1.is_inherent());
775		});
776	}
777
778	#[test]
779	fn should_send_unsigned_with_signed_payload_with_all_account_and_filter() {
780		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
781
782		let mut t = sp_io::TestExternalities::default();
783		t.register_extension(TransactionPoolExt::new(pool));
784
785		// given
786		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
787
788		t.execute_with(|| {
789			// when
790			let result = Signer::<TestRuntime, DummyAppCrypto>::all_accounts()
791				.with_filter(vec![0xf2.into(), 0xf1.into()])
792				.send_unsigned_transaction(
793					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
794					|_payload, _signature| CALL.clone(),
795				);
796
797			// then
798			let mut res = result.into_iter();
799			assert_account(res.next(), 0, 0xf2);
800			assert_account(res.next(), 1, 0xf1);
801			assert_eq!(res.next(), None);
802
803			// check the transaction pool content:
804			let tx1 = pool_state.write().transactions.pop().unwrap();
805			let _tx2 = pool_state.write().transactions.pop().unwrap();
806			assert!(pool_state.read().transactions.is_empty());
807			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
808			assert!(tx1.is_inherent());
809		});
810	}
811
812	#[test]
813	fn should_send_unsigned_with_signed_payload_with_any_account_and_filter() {
814		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
815
816		let mut t = sp_io::TestExternalities::default();
817		t.register_extension(TransactionPoolExt::new(pool));
818
819		// given
820		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
821
822		t.execute_with(|| {
823			// when
824			let result = Signer::<TestRuntime, DummyAppCrypto>::any_account()
825				.with_filter(vec![0xf2.into(), 0xf1.into()])
826				.send_unsigned_transaction(
827					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
828					|_payload, _signature| CALL.clone(),
829				);
830
831			// then
832			let mut res = result.into_iter();
833			assert_account(res.next(), 0, 0xf2);
834			assert_eq!(res.next(), None);
835
836			// check the transaction pool content:
837			let tx1 = pool_state.write().transactions.pop().unwrap();
838			assert!(pool_state.read().transactions.is_empty());
839			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
840			assert!(tx1.is_inherent());
841		});
842	}
843}