fp_self_contained/
checked_extrinsic.rs

1// This file is part of Frontier.
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
18use frame_support::dispatch::{DispatchInfo, GetDispatchInfo};
19use scale_codec::Encode;
20use sp_runtime::{
21	generic::ExtrinsicFormat,
22	traits::{
23		transaction_extension::TransactionExtension, Applyable, AsTransactionAuthorizedOrigin,
24		DispatchInfoOf, DispatchTransaction, Dispatchable, MaybeDisplay, Member,
25		PostDispatchInfoOf, ValidateUnsigned,
26	},
27	transaction_validity::{
28		InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
29	},
30	RuntimeDebug,
31};
32
33use crate::SelfContainedCall;
34
35#[derive(Clone, Eq, PartialEq, RuntimeDebug)]
36pub enum CheckedSignature<AccountId, Extension, SelfContainedSignedInfo> {
37	GenericDelegated(ExtrinsicFormat<AccountId, Extension>),
38	SelfContained(SelfContainedSignedInfo),
39}
40
41/// Definition of something that the external world might want to say; its
42/// existence implies that it has been checked and is good, particularly with
43/// regards to the signature.
44#[derive(Clone, Eq, PartialEq, RuntimeDebug)]
45pub struct CheckedExtrinsic<AccountId, Call, Extension, SelfContainedSignedInfo> {
46	/// Who this purports to be from and the number of extrinsics have come before
47	/// from the same signer, if anyone (note this is not a signature).
48	pub signed: CheckedSignature<AccountId, Extension, SelfContainedSignedInfo>,
49
50	/// The function that should be called.
51	pub function: Call,
52}
53
54impl<AccountId, Call: GetDispatchInfo, Extension, SelfContainedSignedInfo> GetDispatchInfo
55	for CheckedExtrinsic<AccountId, Call, Extension, SelfContainedSignedInfo>
56{
57	fn get_dispatch_info(&self) -> DispatchInfo {
58		self.function.get_dispatch_info()
59	}
60}
61
62impl<AccountId, Call, Extension, SelfContainedSignedInfo, Origin> Applyable
63	for CheckedExtrinsic<AccountId, Call, Extension, SelfContainedSignedInfo>
64where
65	AccountId: Member + MaybeDisplay,
66	Call: Member
67		+ Dispatchable<RuntimeOrigin = Origin>
68		+ Encode
69		+ SelfContainedCall<SignedInfo = SelfContainedSignedInfo>,
70	Extension: TransactionExtension<Call>,
71	Origin: From<Option<AccountId>> + AsTransactionAuthorizedOrigin,
72	SelfContainedSignedInfo: Send + Sync + 'static,
73{
74	type Call = Call;
75
76	fn validate<U: ValidateUnsigned<Call = Self::Call>>(
77		&self,
78		source: TransactionSource,
79		info: &DispatchInfoOf<Self::Call>,
80		len: usize,
81	) -> TransactionValidity {
82		use CheckedSignature::*;
83		match &self.signed {
84			GenericDelegated(format) => match format {
85				ExtrinsicFormat::Bare => {
86					let inherent_validation = U::validate_unsigned(source, &self.function)?;
87					#[allow(deprecated)]
88					let legacy_validation = Extension::bare_validate(&self.function, info, len)?;
89					Ok(legacy_validation.combine_with(inherent_validation))
90				}
91				ExtrinsicFormat::Signed(ref signer, ref extension) => {
92					let origin = Some(signer.clone()).into();
93					extension
94						.validate_only(origin, &self.function, info, len, source, 0)
95						.map(|x| x.0)
96				}
97				ExtrinsicFormat::General(extension_version, ref extension) => extension
98					.validate_only(
99						None.into(),
100						&self.function,
101						info,
102						len,
103						source,
104						*extension_version,
105					)
106					.map(|x| x.0),
107			},
108			SelfContained(signed_info) => self
109				.function
110				.validate_self_contained(signed_info, info, len)
111				.ok_or(TransactionValidityError::Invalid(
112					InvalidTransaction::BadProof,
113				))?,
114		}
115	}
116
117	fn apply<U: ValidateUnsigned<Call = Self::Call>>(
118		self,
119		info: &DispatchInfoOf<Self::Call>,
120		len: usize,
121	) -> sp_runtime::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Self::Call>> {
122		match self.signed {
123			CheckedSignature::GenericDelegated(format) => match format {
124				ExtrinsicFormat::Bare => {
125					U::pre_dispatch(&self.function)?;
126					// TODO: Separate logic from `TransactionExtension` into a new `InherentExtension`
127					// interface.
128					Extension::bare_validate_and_prepare(&self.function, info, len)?;
129					let res = self.function.dispatch(None.into());
130					let mut post_info = res.unwrap_or_else(|err| err.post_info);
131					let pd_res = res.map(|_| ()).map_err(|e| e.error);
132					// TODO: Separate logic from `TransactionExtension` into a new `InherentExtension`
133					// interface.
134					Extension::bare_post_dispatch(info, &mut post_info, len, &pd_res)?;
135					Ok(res)
136				}
137				ExtrinsicFormat::Signed(signer, extension) => {
138					extension.dispatch_transaction(Some(signer).into(), self.function, info, len, 0)
139				}
140				ExtrinsicFormat::General(extension_version, extension) => extension
141					.dispatch_transaction(None.into(), self.function, info, len, extension_version),
142			},
143			CheckedSignature::SelfContained(signed_info) => {
144				// If pre-dispatch fail, the block must be considered invalid
145				self.function
146					.pre_dispatch_self_contained(&signed_info, info, len)
147					.ok_or(TransactionValidityError::Invalid(
148						InvalidTransaction::BadProof,
149					))??;
150				let res = self.function.apply_self_contained(signed_info).ok_or(
151					TransactionValidityError::Invalid(InvalidTransaction::BadProof),
152				)?;
153				let mut post_info = match res {
154					Ok(info) => info,
155					Err(err) => err.post_info,
156				};
157				Extension::bare_post_dispatch(
158					info,
159					&mut post_info,
160					len,
161					&res.map(|_| ()).map_err(|e| e.error),
162				)?;
163				Ok(res)
164			}
165		}
166	}
167}