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<
277		T: SigningTypes + CreateInherent<LocalCall>,
278		C: AppCrypto<T::Public, T::Signature>,
279		LocalCall,
280	> SendUnsignedTransaction<T, LocalCall> for Signer<T, C, ForAny>
281{
282	type Result = Option<(Account<T>, Result<(), ()>)>;
283
284	fn send_unsigned_transaction<TPayload, F>(
285		&self,
286		f: F,
287		f2: impl Fn(TPayload, T::Signature) -> LocalCall,
288	) -> Self::Result
289	where
290		F: Fn(&Account<T>) -> TPayload,
291		TPayload: SignedPayload<T>,
292	{
293		self.for_any(|account| {
294			let payload = f(account);
295			let signature = payload.sign::<C>()?;
296			let call = f2(payload, signature);
297			self.submit_unsigned_transaction(call)
298		})
299	}
300}
301
302impl<
303		T: SigningTypes + CreateInherent<LocalCall>,
304		C: AppCrypto<T::Public, T::Signature>,
305		LocalCall,
306	> SendUnsignedTransaction<T, LocalCall> for Signer<T, C, ForAll>
307{
308	type Result = Vec<(Account<T>, Result<(), ()>)>;
309
310	fn send_unsigned_transaction<TPayload, F>(
311		&self,
312		f: F,
313		f2: impl Fn(TPayload, T::Signature) -> LocalCall,
314	) -> Self::Result
315	where
316		F: Fn(&Account<T>) -> TPayload,
317		TPayload: SignedPayload<T>,
318	{
319		self.for_all(|account| {
320			let payload = f(account);
321			let signature = payload.sign::<C>()?;
322			let call = f2(payload, signature);
323			self.submit_unsigned_transaction(call)
324		})
325	}
326}
327
328/// Details of an account for which a private key is contained in the keystore.
329#[derive(RuntimeDebug, PartialEq)]
330pub struct Account<T: SigningTypes> {
331	/// Index on the provided list of accounts or list of all accounts.
332	pub index: usize,
333	/// Runtime-specific `AccountId`.
334	pub id: T::AccountId,
335	/// A runtime-specific `Public` key for that key pair.
336	pub public: T::Public,
337}
338
339impl<T: SigningTypes> Account<T> {
340	/// Create a new Account instance
341	pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self {
342		Self { index, id, public }
343	}
344}
345
346impl<T: SigningTypes> Clone for Account<T>
347where
348	T::AccountId: Clone,
349	T::Public: Clone,
350{
351	fn clone(&self) -> Self {
352		Self { index: self.index, id: self.id.clone(), public: self.public.clone() }
353	}
354}
355
356/// A type binding runtime-level `Public/Signature` pair with crypto wrapped by `RuntimeAppPublic`.
357///
358/// Implementations of this trait should specify the app-specific public/signature types.
359/// This is merely a wrapper around an existing `RuntimeAppPublic` type, but with
360/// extra non-application-specific crypto type that is being wrapped (e.g. `sr25519`, `ed25519`).
361/// This is needed to later on convert into runtime-specific `Public` key, which might support
362/// multiple different crypto.
363/// The point of this trait is to be able to easily convert between `RuntimeAppPublic`, the wrapped
364/// (generic = non application-specific) crypto types and the `Public` type required by the runtime.
365///
366/// Example (pseudo-)implementation:
367/// ```ignore
368/// // im-online specific crypto
369/// type RuntimeAppPublic = ImOnline(sr25519::Public);
370///
371/// // wrapped "raw" crypto
372/// type GenericPublic = sr25519::Public;
373/// type GenericSignature = sr25519::Signature;
374///
375/// // runtime-specific public key
376/// type Public = MultiSigner: From<sr25519::Public>;
377/// type Signature = MultiSignature: From<sr25519::Signature>;
378/// ```
379// TODO [#5662] Potentially use `IsWrappedBy` types, or find some other way to make it easy to
380// obtain unwrapped crypto (and wrap it back).
381pub trait AppCrypto<Public, Signature> {
382	/// A application-specific crypto.
383	type RuntimeAppPublic: RuntimeAppPublic;
384
385	/// A raw crypto public key wrapped by `RuntimeAppPublic`.
386	type GenericPublic: From<Self::RuntimeAppPublic>
387		+ Into<Self::RuntimeAppPublic>
388		+ TryFrom<Public>
389		+ Into<Public>;
390
391	/// A matching raw crypto `Signature` type.
392	type GenericSignature: From<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>
393		+ Into<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>
394		+ TryFrom<Signature>
395		+ Into<Signature>;
396
397	/// Sign payload with the private key to maps to the provided public key.
398	fn sign(payload: &[u8], public: Public) -> Option<Signature> {
399		let p: Self::GenericPublic = public.try_into().ok()?;
400		let x = Into::<Self::RuntimeAppPublic>::into(p);
401		x.sign(&payload)
402			.map(|x| {
403				let sig: Self::GenericSignature = x.into();
404				sig
405			})
406			.map(Into::into)
407	}
408
409	/// Verify signature against the provided public key.
410	fn verify(payload: &[u8], public: Public, signature: Signature) -> bool {
411		let p: Self::GenericPublic = match public.try_into() {
412			Ok(a) => a,
413			_ => return false,
414		};
415		let x = Into::<Self::RuntimeAppPublic>::into(p);
416		let signature: Self::GenericSignature = match signature.try_into() {
417			Ok(a) => a,
418			_ => return false,
419		};
420		let signature =
421			Into::<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>::into(signature);
422
423		x.verify(&payload, &signature)
424	}
425}
426
427/// A wrapper around the types which are used for signing.
428///
429/// This trait adds extra bounds to `Public` and `Signature` types of the runtime
430/// that are necessary to use these types for signing.
431// TODO [#5663] Could this be just `T::Signature as traits::Verify>::Signer`?
432// Seems that this may cause issues with bounds resolution.
433pub trait SigningTypes: crate::Config {
434	/// A public key that is capable of identifying `AccountId`s.
435	///
436	/// Usually that's either a raw crypto public key (e.g. `sr25519::Public`) or
437	/// an aggregate type for multiple crypto public keys, like `MultiSigner`.
438	type Public: Clone
439		+ PartialEq
440		+ IdentifyAccount<AccountId = Self::AccountId>
441		+ core::fmt::Debug
442		+ codec::Codec
443		+ Ord
444		+ scale_info::TypeInfo;
445
446	/// A matching `Signature` type.
447	type Signature: Clone + PartialEq + core::fmt::Debug + codec::Codec + scale_info::TypeInfo;
448}
449
450/// Common interface for the `CreateTransaction` trait family to unify the `Call` type.
451pub trait CreateTransactionBase<LocalCall> {
452	/// The extrinsic.
453	type Extrinsic: ExtrinsicLike + Encode;
454
455	/// The runtime's call type.
456	///
457	/// This has additional bound to be able to be created from pallet-local `Call` types.
458	type RuntimeCall: From<LocalCall> + Encode;
459}
460
461/// Interface for creating a transaction.
462pub trait CreateTransaction<LocalCall>: CreateTransactionBase<LocalCall> {
463	/// The extension.
464	type Extension: TypeInfo;
465
466	/// Create a transaction using the call and the desired transaction extension.
467	fn create_transaction(
468		call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall,
469		extension: Self::Extension,
470	) -> Self::Extrinsic;
471}
472
473/// Interface for creating an old-school signed transaction.
474pub trait CreateSignedTransaction<LocalCall>:
475	CreateTransactionBase<LocalCall> + SigningTypes
476{
477	/// Attempt to create signed extrinsic data that encodes call from given account.
478	///
479	/// Runtime implementation is free to construct the payload to sign and the signature
480	/// in any way it wants.
481	/// Returns `None` if signed extrinsic could not be created (either because signing failed
482	/// or because of any other runtime-specific reason).
483	fn create_signed_transaction<C: AppCrypto<Self::Public, Self::Signature>>(
484		call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall,
485		public: Self::Public,
486		account: Self::AccountId,
487		nonce: Self::Nonce,
488	) -> Option<Self::Extrinsic>;
489}
490
491/// Interface for creating an inherent.
492pub trait CreateInherent<LocalCall>: CreateTransactionBase<LocalCall> {
493	/// Create an inherent.
494	fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic;
495}
496
497/// A message signer.
498pub trait SignMessage<T: SigningTypes> {
499	/// A signature data.
500	///
501	/// May contain account used for signing and the `Signature` itself.
502	type SignatureData;
503
504	/// Sign a message.
505	///
506	/// Implementation of this method should return
507	/// a result containing the signature.
508	fn sign_message(&self, message: &[u8]) -> Self::SignatureData;
509
510	/// Construct and sign given payload.
511	///
512	/// This method expects `f` to return a `SignedPayload`
513	/// object which is then used for signing.
514	fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
515	where
516		F: Fn(&Account<T>) -> TPayload,
517		TPayload: SignedPayload<T>;
518}
519
520/// Submit a signed transaction to the transaction pool.
521pub trait SendSignedTransaction<
522	T: CreateSignedTransaction<LocalCall>,
523	C: AppCrypto<T::Public, T::Signature>,
524	LocalCall,
525>
526{
527	/// A submission result.
528	///
529	/// This should contain an indication of success and the account that was used for signing.
530	type Result;
531
532	/// Submit a signed transaction to the local pool.
533	///
534	/// Given `f` closure will be called for every requested account and expects a `Call` object
535	/// to be returned.
536	/// The call is then wrapped into a transaction (see `#CreateSignedTransaction`), signed and
537	/// submitted to the pool.
538	fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result;
539
540	/// Wraps the call into transaction, signs using given account and submits to the pool.
541	fn send_single_signed_transaction(
542		&self,
543		account: &Account<T>,
544		call: LocalCall,
545	) -> Option<Result<(), ()>> {
546		let mut account_data = crate::Account::<T>::get(&account.id);
547		log::debug!(
548			target: "runtime::offchain",
549			"Creating signed transaction from account: {:?} (nonce: {:?})",
550			account.id,
551			account_data.nonce,
552		);
553		let transaction = T::create_signed_transaction::<C>(
554			call.into(),
555			account.public.clone(),
556			account.id.clone(),
557			account_data.nonce,
558		)?;
559
560		let res = SubmitTransaction::<T, LocalCall>::submit_transaction(transaction);
561
562		if res.is_ok() {
563			// increment the nonce. This is fine, since the code should always
564			// be running in off-chain context, so we NEVER persists data.
565			account_data.nonce += One::one();
566			crate::Account::<T>::insert(&account.id, account_data);
567		}
568
569		Some(res)
570	}
571}
572
573/// Submit an unsigned transaction onchain with a signed payload
574pub trait SendUnsignedTransaction<T: SigningTypes + CreateInherent<LocalCall>, LocalCall> {
575	/// A submission result.
576	///
577	/// Should contain the submission result and the account(s) that signed the payload.
578	type Result;
579
580	/// Send an unsigned transaction with a signed payload.
581	///
582	/// This method takes `f` and `f2` where:
583	/// - `f` is called for every account and is expected to return a `SignedPayload` object.
584	/// - `f2` is then called with the `SignedPayload` returned by `f` and the signature and is
585	/// expected to return a `Call` object to be embedded into transaction.
586	fn send_unsigned_transaction<TPayload, F>(
587		&self,
588		f: F,
589		f2: impl Fn(TPayload, T::Signature) -> LocalCall,
590	) -> Self::Result
591	where
592		F: Fn(&Account<T>) -> TPayload,
593		TPayload: SignedPayload<T>;
594
595	/// Submits an unsigned call to the transaction pool.
596	fn submit_unsigned_transaction(&self, call: LocalCall) -> Option<Result<(), ()>> {
597		let xt = T::create_inherent(call.into());
598		Some(SubmitTransaction::<T, LocalCall>::submit_transaction(xt))
599	}
600}
601
602/// Utility trait to be implemented on payloads that can be signed.
603pub trait SignedPayload<T: SigningTypes>: Encode {
604	/// Return a public key that is expected to have a matching key in the keystore,
605	/// which should be used to sign the payload.
606	fn public(&self) -> T::Public;
607
608	/// Sign the payload using the implementor's provided public key.
609	///
610	/// Returns `Some(signature)` if public key is supported.
611	fn sign<C: AppCrypto<T::Public, T::Signature>>(&self) -> Option<T::Signature> {
612		self.using_encoded(|payload| C::sign(payload, self.public()))
613	}
614
615	/// Verify signature against payload.
616	///
617	/// Returns a bool indicating whether the signature is valid or not.
618	fn verify<C: AppCrypto<T::Public, T::Signature>>(&self, signature: T::Signature) -> bool {
619		self.using_encoded(|payload| C::verify(payload, self.public(), signature))
620	}
621}
622
623#[cfg(test)]
624mod tests {
625	use super::*;
626	use crate::mock::{RuntimeCall, Test as TestRuntime, CALL};
627	use codec::Decode;
628	use sp_core::offchain::{testing, TransactionPoolExt};
629	use sp_runtime::testing::{TestSignature, TestXt, UintAuthorityId};
630
631	impl SigningTypes for TestRuntime {
632		type Public = UintAuthorityId;
633		type Signature = TestSignature;
634	}
635
636	type Extrinsic = TestXt<RuntimeCall, ()>;
637
638	impl CreateTransactionBase<RuntimeCall> for TestRuntime {
639		type Extrinsic = Extrinsic;
640		type RuntimeCall = RuntimeCall;
641	}
642
643	impl CreateInherent<RuntimeCall> for TestRuntime {
644		fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic {
645			Extrinsic::new_bare(call)
646		}
647	}
648
649	#[derive(codec::Encode, codec::Decode)]
650	struct SimplePayload {
651		pub public: UintAuthorityId,
652		pub data: Vec<u8>,
653	}
654
655	impl SignedPayload<TestRuntime> for SimplePayload {
656		fn public(&self) -> UintAuthorityId {
657			self.public.clone()
658		}
659	}
660
661	struct DummyAppCrypto;
662	// Bind together the `SigningTypes` with app-crypto and the wrapper types.
663	// here the implementation is pretty dummy, because we use the same type for
664	// both application-specific crypto and the runtime crypto, but in real-life
665	// runtimes it's going to use different types everywhere.
666	impl AppCrypto<UintAuthorityId, TestSignature> for DummyAppCrypto {
667		type RuntimeAppPublic = UintAuthorityId;
668		type GenericPublic = UintAuthorityId;
669		type GenericSignature = TestSignature;
670	}
671
672	fn assert_account(next: Option<(Account<TestRuntime>, Result<(), ()>)>, index: usize, id: u64) {
673		assert_eq!(next, Some((Account { index, id, public: id.into() }, Ok(()))));
674	}
675
676	#[test]
677	fn should_send_unsigned_with_signed_payload_with_all_accounts() {
678		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
679
680		let mut t = sp_io::TestExternalities::default();
681		t.register_extension(TransactionPoolExt::new(pool));
682
683		// given
684		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
685
686		t.execute_with(|| {
687			// when
688			let result = Signer::<TestRuntime, DummyAppCrypto>::all_accounts()
689				.send_unsigned_transaction(
690					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
691					|_payload, _signature| CALL.clone(),
692				);
693
694			// then
695			let mut res = result.into_iter();
696			assert_account(res.next(), 0, 0xf0);
697			assert_account(res.next(), 1, 0xf1);
698			assert_account(res.next(), 2, 0xf2);
699			assert_eq!(res.next(), None);
700
701			// check the transaction pool content:
702			let tx1 = pool_state.write().transactions.pop().unwrap();
703			let _tx2 = pool_state.write().transactions.pop().unwrap();
704			let _tx3 = pool_state.write().transactions.pop().unwrap();
705			assert!(pool_state.read().transactions.is_empty());
706			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
707			assert!(tx1.is_inherent());
708		});
709	}
710
711	#[test]
712	fn should_send_unsigned_with_signed_payload_with_any_account() {
713		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
714
715		let mut t = sp_io::TestExternalities::default();
716		t.register_extension(TransactionPoolExt::new(pool));
717
718		// given
719		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
720
721		t.execute_with(|| {
722			// when
723			let result = Signer::<TestRuntime, DummyAppCrypto>::any_account()
724				.send_unsigned_transaction(
725					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
726					|_payload, _signature| CALL.clone(),
727				);
728
729			// then
730			let mut res = result.into_iter();
731			assert_account(res.next(), 0, 0xf0);
732			assert_eq!(res.next(), None);
733
734			// check the transaction pool content:
735			let tx1 = pool_state.write().transactions.pop().unwrap();
736			assert!(pool_state.read().transactions.is_empty());
737			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
738			assert!(tx1.is_inherent());
739		});
740	}
741
742	#[test]
743	fn should_send_unsigned_with_signed_payload_with_all_account_and_filter() {
744		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
745
746		let mut t = sp_io::TestExternalities::default();
747		t.register_extension(TransactionPoolExt::new(pool));
748
749		// given
750		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
751
752		t.execute_with(|| {
753			// when
754			let result = Signer::<TestRuntime, DummyAppCrypto>::all_accounts()
755				.with_filter(vec![0xf2.into(), 0xf1.into()])
756				.send_unsigned_transaction(
757					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
758					|_payload, _signature| CALL.clone(),
759				);
760
761			// then
762			let mut res = result.into_iter();
763			assert_account(res.next(), 0, 0xf2);
764			assert_account(res.next(), 1, 0xf1);
765			assert_eq!(res.next(), None);
766
767			// check the transaction pool content:
768			let tx1 = pool_state.write().transactions.pop().unwrap();
769			let _tx2 = pool_state.write().transactions.pop().unwrap();
770			assert!(pool_state.read().transactions.is_empty());
771			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
772			assert!(tx1.is_inherent());
773		});
774	}
775
776	#[test]
777	fn should_send_unsigned_with_signed_payload_with_any_account_and_filter() {
778		let (pool, pool_state) = testing::TestTransactionPoolExt::new();
779
780		let mut t = sp_io::TestExternalities::default();
781		t.register_extension(TransactionPoolExt::new(pool));
782
783		// given
784		UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
785
786		t.execute_with(|| {
787			// when
788			let result = Signer::<TestRuntime, DummyAppCrypto>::any_account()
789				.with_filter(vec![0xf2.into(), 0xf1.into()])
790				.send_unsigned_transaction(
791					|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
792					|_payload, _signature| CALL.clone(),
793				);
794
795			// then
796			let mut res = result.into_iter();
797			assert_account(res.next(), 0, 0xf2);
798			assert_eq!(res.next(), None);
799
800			// check the transaction pool content:
801			let tx1 = pool_state.write().transactions.pop().unwrap();
802			assert!(pool_state.read().transactions.is_empty());
803			let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
804			assert!(tx1.is_inherent());
805		});
806	}
807}