referrerpolicy=no-referrer-when-downgrade

pallet_verify_signature/
extension.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//! Transaction extension which validates a signature against a payload constructed from a call and
19//! the rest of the transaction extension pipeline.
20
21use crate::{Config, WeightInfo};
22use codec::{Decode, DecodeWithMemTracking, Encode};
23use frame_support::{pallet_prelude::TransactionSource, traits::OriginTrait};
24use scale_info::TypeInfo;
25use sp_io::hashing::blake2_256;
26use sp_runtime::{
27	impl_tx_ext_default,
28	traits::{
29		transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin, DispatchInfoOf,
30		Dispatchable, Verify,
31	},
32	transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
33};
34use sp_weights::Weight;
35
36/// Extension that, if enabled, validates a signature type against the payload constructed from the
37/// call and the rest of the transaction extension pipeline. This extension provides the
38/// functionality that traditionally signed transactions had with the implicit signature checking
39/// implemented in [`Checkable`](sp_runtime::traits::Checkable). It is meant to be placed ahead of
40/// any other extensions that do authorization work in the [`TransactionExtension`] pipeline.
41#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
42#[scale_info(skip_type_params(T))]
43pub enum VerifySignature<T>
44where
45	T: Config + Send + Sync,
46{
47	/// The extension will verify the signature and, if successful, authorize a traditionally
48	/// signed transaction.
49	Signed {
50		/// The signature provided by the transaction submitter.
51		signature: T::Signature,
52		/// The account that signed the payload.
53		account: T::AccountId,
54	},
55	/// The extension is disabled and will be passthrough.
56	Disabled,
57}
58
59impl<T> core::fmt::Debug for VerifySignature<T>
60where
61	T: Config + Send + Sync,
62{
63	#[cfg(feature = "std")]
64	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
65		write!(f, "VerifySignature")
66	}
67
68	#[cfg(not(feature = "std"))]
69	fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
70		Ok(())
71	}
72}
73
74impl<T> VerifySignature<T>
75where
76	T: Config + Send + Sync,
77{
78	/// Create a new extension instance that will validate the provided signature.
79	pub fn new_with_signature(signature: T::Signature, account: T::AccountId) -> Self {
80		Self::Signed { signature, account }
81	}
82
83	/// Create a new passthrough extension instance.
84	pub fn new_disabled() -> Self {
85		Self::Disabled
86	}
87}
88
89impl<T> TransactionExtension<T::RuntimeCall> for VerifySignature<T>
90where
91	T: Config + Send + Sync,
92	<T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
93{
94	const IDENTIFIER: &'static str = "VerifyMultiSignature";
95	type Implicit = ();
96	type Val = ();
97	type Pre = ();
98
99	fn weight(&self, _call: &T::RuntimeCall) -> Weight {
100		match &self {
101			// The benchmarked weight of the payload construction and signature checking.
102			Self::Signed { .. } => T::WeightInfo::verify_signature(),
103			// When the extension is passthrough, it consumes no weight.
104			Self::Disabled => Weight::zero(),
105		}
106	}
107
108	fn validate(
109		&self,
110		mut origin: <T::RuntimeCall as Dispatchable>::RuntimeOrigin,
111		_call: &T::RuntimeCall,
112		_info: &DispatchInfoOf<T::RuntimeCall>,
113		_len: usize,
114		_: (),
115		inherited_implication: &impl Encode,
116		_source: TransactionSource,
117	) -> Result<
118		(ValidTransaction, Self::Val, <T::RuntimeCall as Dispatchable>::RuntimeOrigin),
119		TransactionValidityError,
120	> {
121		// If the extension is disabled, return early.
122		let (signature, account) = match &self {
123			Self::Signed { signature, account } => (signature, account),
124			Self::Disabled => return Ok((Default::default(), (), origin)),
125		};
126
127		// This extension must receive an unauthorized origin as it is meant to headline the
128		// authorization extension pipeline. Any extensions that precede this one must not authorize
129		// any origin and serve some other functional purpose.
130		if origin.is_transaction_authorized() {
131			return Err(InvalidTransaction::BadSigner.into());
132		}
133
134		// Construct the payload that the signature will be validated against. The inherited
135		// implication contains the encoded bytes of the call and all of the extension data of the
136		// extensions that follow in the `TransactionExtension` pipeline.
137		//
138		// In other words:
139		// - extensions that precede this extension are ignored in terms of signature validation;
140		// - extensions that follow this extension are included in the payload to be signed (as if
141		//   they were the entire `SignedExtension` pipeline in the traditional signed transaction
142		//   model).
143		//
144		// The encoded bytes of the payload are then hashed using `blake2_256`.
145		let msg = inherited_implication.using_encoded(blake2_256);
146
147		// The extension was enabled, so the signature must match.
148		if !signature.verify(&msg[..], account) {
149			Err(InvalidTransaction::BadProof)?
150		}
151
152		// Return the signer as the transaction origin.
153		origin.set_caller_from_signed(account.clone());
154		Ok((ValidTransaction::default(), (), origin))
155	}
156
157	impl_tx_ext_default!(T::RuntimeCall; prepare);
158}