Skip to main content

sp_runtime/generic/
checked_extrinsic.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//! Generic implementation of an extrinsic that has passed the verification
19//! stage.
20
21use super::unchecked_extrinsic::ExtensionVersion;
22use crate::{
23	traits::{
24		self, AsTransactionAuthorizedOrigin, DispatchInfoOf, DispatchTransaction, Dispatchable,
25		ExtensionVariant, InvalidVersion, MaybeDisplay, Member, Pipeline, PostDispatchInfoOf,
26		TransactionExtension,
27	},
28	transaction_validity::{TransactionSource, TransactionValidity},
29};
30use codec::Encode;
31use sp_weights::Weight;
32
33/// Version 0 of the transaction extension version.
34const EXTENSION_V0_VERSION: ExtensionVersion = 0;
35
36/// The kind of extrinsic this is, including any fields required of that kind. This is basically
37/// the full extrinsic except the `Call`.
38///
39/// Bare extrinsics and signed extrinsics are extended with the transaction extension version 0,
40/// specified by the generic parameter `ExtensionV0`.
41///
42/// General extrinsics support multiple transaction extension version, specified by both
43/// `ExtensionV0` and `ExtensionOtherVersions`, by default `ExtensionOtherVersions` is set to
44/// invalid version, making `ExtensionV0` the only supported version. If you want to support more
45/// versions, you need to specify the `ExtensionOtherVersions` parameter.
46#[derive(PartialEq, Eq, Clone, Debug)]
47pub enum ExtrinsicFormat<AccountId, ExtensionV0, ExtensionOtherVersions = InvalidVersion> {
48	/// Extrinsic is bare; it must pass either the bare forms of `TransactionExtension` or
49	/// `ValidateUnsigned`, both deprecated, or alternatively a `ProvideInherent`.
50	Bare,
51	/// Extrinsic has a default `Origin` of `Signed(AccountId)` and must pass all
52	/// `TransactionExtension`s regular checks and includes all extension data.
53	Signed(AccountId, ExtensionV0),
54	/// Extrinsic has a default `Origin` of `None` and must pass all `TransactionExtension`s.
55	/// regular checks and includes all extension data.
56	General(ExtensionVariant<ExtensionV0, ExtensionOtherVersions>),
57}
58
59/// Definition of something that the external world might want to say; its existence implies that it
60/// has been checked and is good, particularly with regards to the signature.
61///
62/// This is typically passed into [`traits::Applyable::apply`], which should execute
63/// [`CheckedExtrinsic::function`], alongside all other bits and bobs.
64///
65/// Bare extrinsics and signed extrinsics are extended with the transaction extension version 0,
66/// specified by the generic parameter `ExtensionV0`.
67///
68/// General extrinsics support multiple transaction extension versions, specified by both
69/// `ExtensionV0` and `ExtensionOtherVersions`, by default `ExtensionOtherVersions` is set to
70/// invalid version, making `ExtensionV0` the only supported version. If you want to support more
71/// versions, you need to specify the `ExtensionOtherVersions` parameter.
72#[derive(PartialEq, Eq, Clone, Debug)]
73pub struct CheckedExtrinsic<AccountId, Call, ExtensionV0, ExtensionOtherVersions = InvalidVersion> {
74	/// Who this purports to be from and the number of extrinsics have come before
75	/// from the same signer, if anyone (note this is not a signature).
76	pub format: ExtrinsicFormat<AccountId, ExtensionV0, ExtensionOtherVersions>,
77
78	/// The function that should be called.
79	pub function: Call,
80}
81
82impl<AccountId, Call, ExtensionV0, ExtensionOtherVersions, RuntimeOrigin> traits::Applyable
83	for CheckedExtrinsic<AccountId, Call, ExtensionV0, ExtensionOtherVersions>
84where
85	AccountId: Member + MaybeDisplay,
86	Call: Member + Dispatchable<RuntimeOrigin = RuntimeOrigin> + Encode,
87	ExtensionV0: TransactionExtension<Call>,
88	ExtensionOtherVersions: Pipeline<Call>,
89	RuntimeOrigin: From<Option<AccountId>> + AsTransactionAuthorizedOrigin,
90{
91	type Call = Call;
92
93	#[allow(deprecated)]
94	fn validate<I: crate::traits::ValidateUnsigned<Call = Self::Call>>(
95		&self,
96		source: TransactionSource,
97		info: &DispatchInfoOf<Self::Call>,
98		len: usize,
99	) -> TransactionValidity {
100		match self.format {
101			ExtrinsicFormat::Bare => {
102				let inherent_validation = I::validate_unsigned(source, &self.function)?;
103				let legacy_validation = ExtensionV0::bare_validate(&self.function, info, len)?;
104				Ok(legacy_validation.combine_with(inherent_validation))
105			},
106			ExtrinsicFormat::Signed(ref signer, ref extension) => {
107				let origin = Some(signer.clone()).into();
108				extension
109					.validate_only(origin, &self.function, info, len, source, EXTENSION_V0_VERSION)
110					.map(|x| x.0)
111			},
112			ExtrinsicFormat::General(ref extension) => {
113				extension.validate_only(None.into(), &self.function, info, len, source)
114			},
115		}
116	}
117
118	#[allow(deprecated)]
119	fn apply<I: crate::traits::ValidateUnsigned<Call = Self::Call>>(
120		self,
121		info: &DispatchInfoOf<Self::Call>,
122		len: usize,
123	) -> crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Self::Call>> {
124		match self.format {
125			ExtrinsicFormat::Bare => {
126				I::pre_dispatch(&self.function)?;
127				// TODO: Separate logic from `TransactionExtension` into a new `InherentExtension`
128				// interface.
129				ExtensionV0::bare_validate_and_prepare(&self.function, info, len)?;
130				let res = self.function.dispatch(None.into());
131				let mut post_info = res.unwrap_or_else(|err| err.post_info);
132				let pd_res = res.map(|_| ()).map_err(|e| e.error);
133				// TODO: Separate logic from `TransactionExtension` into a new `InherentExtension`
134				// interface.
135				ExtensionV0::bare_post_dispatch(info, &mut post_info, len, &pd_res)?;
136				Ok(res)
137			},
138			ExtrinsicFormat::Signed(signer, extension) => extension.dispatch_transaction(
139				Some(signer).into(),
140				self.function,
141				info,
142				len,
143				EXTENSION_V0_VERSION,
144			),
145			ExtrinsicFormat::General(extension) => {
146				extension.dispatch_transaction(None.into(), self.function, info, len)
147			},
148		}
149	}
150}
151
152impl<AccountId, Call, ExtensionV0, ExtensionOtherVersions>
153	CheckedExtrinsic<AccountId, Call, ExtensionV0, ExtensionOtherVersions>
154where
155	Call: Dispatchable + Encode,
156	ExtensionV0: TransactionExtension<Call>,
157	ExtensionOtherVersions: Pipeline<Call>,
158	<Call as Dispatchable>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
159{
160	/// Returns the weight of the extension of this transaction, if present. If the transaction
161	/// doesn't use any extension, the weight returned is equal to zero.
162	pub fn extension_weight(&self) -> Weight {
163		match &self.format {
164			ExtrinsicFormat::Bare => Weight::zero(),
165			ExtrinsicFormat::Signed(_, ext) => ext.weight(&self.function),
166			ExtrinsicFormat::General(ext) => ext.weight(&self.function),
167		}
168	}
169}