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